diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000000000000000000000000000000000000..2e88b7aa0a96fc6fbc17ccfc6b282363de5a7596
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,3 @@
+We’re closing our issue tracker on GitHub so we can focus on the GitLab.com project and respond to issues more quickly.
+
+We encourage you to open an issue on the [GitLab.com issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues). You can log into GitLab.com using your GitHub account.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000000000000000000000000000000000000..c3b0402644040b93bd08dba93972a0f00d7891b6
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,3 @@
+Thank you for taking the time to contribute back to GitLab!
+
+Please open a merge request [on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests), we look forward to reviewing your contribution! You can log into GitLab.com using your GitHub account.
diff --git a/.gitignore b/.gitignore
index 8f861d76a373b764865e8201dcde465f1d5c9ef7..ce6a363fe35fe4c328b28c79407bc424681116a5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,46 +4,46 @@
 .bundle
 .chef
 .directory
-.envrc
-.gitlab_shell_secret
+/.envrc
+/.gitlab_shell_secret
 .idea
-.rbenv-version
+/.rbenv-version
 .rbx/
-.ruby-gemset
-.ruby-version
-.rvmrc
+/.ruby-gemset
+/.ruby-version
+/.rvmrc
 .sass-cache/
-.secret
-.vagrant
-.byebug_history
-Vagrantfile
-backups/*
-config/aws.yml
-config/database.yml
-config/gitlab.yml
-config/gitlab_ci.yml
-config/initializers/rack_attack.rb
-config/initializers/smtp_settings.rb
-config/initializers/relative_url.rb
-config/resque.yml
-config/unicorn.rb
-config/secrets.yml
-config/sidekiq.yml
-coverage/*
-db/*.sqlite3
-db/*.sqlite3-journal
-db/data.yml
-doc/code/*
-dump.rdb
-log/*.log*
-nohup.out
-public/assets/
-public/uploads.*
-public/uploads/
-shared/artifacts/
-rails_best_practices_output.html
+/.secret
+/.vagrant
+/.byebug_history
+/Vagrantfile
+/backups/*
+/config/aws.yml
+/config/database.yml
+/config/gitlab.yml
+/config/gitlab_ci.yml
+/config/initializers/rack_attack.rb
+/config/initializers/smtp_settings.rb
+/config/initializers/relative_url.rb
+/config/resque.yml
+/config/unicorn.rb
+/config/secrets.yml
+/config/sidekiq.yml
+/coverage/*
+/db/*.sqlite3
+/db/*.sqlite3-journal
+/db/data.yml
+/doc/code/*
+/dump.rdb
+/log/*.log*
+/nohup.out
+/public/assets/
+/public/uploads.*
+/public/uploads/
+/shared/artifacts/
+/rails_best_practices_output.html
 /tags
-tmp/
-vendor/bundle/*
-builds/*
-shared/*
+/tmp/*
+/vendor/bundle/*
+/builds/*
+/shared/*
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e7c45b2e6466d3b31f2cfa595318efb294713a47..d5ac8eb17d2312bc5cadc8eee6f581524c257f87 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,243 +2,213 @@ image: "ruby:2.1"
 
 services:
   - mysql:latest
-  - redis:latest
   - elasticsearch:latest
+  - redis:alpine
 
 cache:
   key: "ruby21"
   paths:
-  - vendor
+  - vendor/apt
+  - vendor/ruby
 
 variables:
   MYSQL_ALLOW_EMPTY_PASSWORD: "1"
   # retry tests only in CI environment
   RSPEC_RETRY_RETRY_COUNT: "3"
   ELASTIC_HOST: "elasticsearch"
-
+  RAILS_ENV: "test"
+  SIMPLECOV: "true"
+  USE_DB: "true"
+  USE_BUNDLE_INSTALL: "true"
 
 before_script:
   - source ./scripts/prepare_build.sh
-  - ruby -v
-  - which ruby
-  - retry gem install bundler --no-ri --no-rdoc
   - cp config/gitlab.yml.example config/gitlab.yml
-  - touch log/application.log
-  - touch log/test.log
-  - retry bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}"
-  - RAILS_ENV=test bundle exec rake db:drop db:create db:schema:load db:migrate
+  - bundle --version
+  - '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}"'
+  - retry gem install knapsack
+  - '[ "$USE_DB" != "true" ] || bundle exec rake db:drop db:create db:schema:load db:migrate'
 
 stages:
+- prepare
 - test
-- notifications
-
-spec:feature:
-  stage: test
-  script:
-    - RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
-    - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:feature
-
-spec:api:
-  stage: test
-  script:
-    - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:api
-
-spec:models:
-  stage: test
-  script:
-    - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models
-
-spec:lib:
-  stage: test
-  script:
-    - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib
-
-spec:services:
-  stage: test
-  script:
-    - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services
-
-spec:other:
-  stage: test
-  script:
-    - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:other
-
-spinach:project:half:
-  stage: test
-  script:
-    - RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
-    - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half
+- post-test
 
-spinach:project:rest:
-  stage: test
-  script:
-    - RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
-    - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest
+# Prepare and merge knapsack tests
 
-spinach:other:
-  stage: test
-  script:
-    - RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
-    - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other
-
-teaspoon:
-  stage: test
-  script:
-    - RAILS_ENV=test bundle exec teaspoon
-
-rubocop:
-  stage: test
-  script:
-    - bundle exec rubocop
-
-scss-lint:
-  stage: test
-  script:
-    - bundle exec rake scss_lint
+.knapsack-state: &knapsack-state
+  services: []
+  variables:
+    USE_DB: "false"
+    USE_BUNDLE_INSTALL: "false"
+  cache:
+    key: "knapsack"
+    paths:
+    - knapsack/
+  artifacts:
+    paths:
+    - knapsack/
 
-brakeman:
-  stage: test
+knapsack:
+  <<: *knapsack-state
+  stage: prepare
   script:
-    - bundle exec rake brakeman
+    - mkdir -p knapsack/
+    - '[[ -f knapsack/rspec_report.json ]] || echo "{}" > knapsack/rspec_report.json'
+    - '[[ -f knapsack/spinach_report.json ]] || echo "{}" > knapsack/spinach_report.json'
 
-flog:
-  stage: test
+update-knapsack:
+  <<: *knapsack-state
+  stage: post-test
   script:
-    - bundle exec rake flog
-
-flay:
-  stage: test
-  script:
-    - bundle exec rake flay
-
-bundler:audit:
-  stage: test
+    - scripts/merge-reports knapsack/rspec_report.json knapsack/rspec_node_*.json
+    - scripts/merge-reports knapsack/spinach_report.json knapsack/spinach_node_*.json
+    - rm -f knapsack/*_node_*.json
   only:
     - master
-  script:
-    - "bundle exec bundle-audit check --update --ignore OSVDB-115941"
 
-db-migrate-reset:
-  stage: test
-  script:
-    - RAILS_ENV=test bundle exec rake db:migrate:reset
-
-# Ruby 2.2 jobs
+# Execute all testing suites
 
-spec:feature:ruby22:
+.rspec-knapsack: &rspec-knapsack
   stage: test
-  image: ruby:2.2
-  only:
-    - master
   script:
-    - RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
-    - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:feature
-  cache:
-    key: "ruby22"
+    - bundle exec rake assets:precompile 2>/dev/null
+    - JOB_NAME=( $CI_BUILD_NAME )
+    - export CI_NODE_INDEX=${JOB_NAME[1]}
+    - export CI_NODE_TOTAL=${JOB_NAME[2]}
+    - export KNAPSACK_REPORT_PATH=knapsack/rspec_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
+    - export KNAPSACK_GENERATE_REPORT=true
+    - cp knapsack/rspec_report.json ${KNAPSACK_REPORT_PATH}
+    - knapsack rspec
+  artifacts:
     paths:
-    - vendor
-
-spec:api:ruby22:
-  stage: test
-  image: ruby:2.2
-  only:
-  - master
-  script:
-    - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:api
-  cache:
-    key: "ruby22"
-    paths:
-    - vendor
+    - knapsack/
 
-spec:models:ruby22:
+.spinach-knapsack: &spinach-knapsack
   stage: test
-  image: ruby:2.2
-  only:
-  - master
   script:
-    - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models
-  cache:
-    key: "ruby22"
-    paths:
-    - vendor
-
-spec:lib:ruby22:
-  stage: test
-  image: ruby:2.2
-  only:
-  - master
-  script:
-    - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib
-  cache:
-    key: "ruby22"
-    paths:
-    - vendor
-
-spec:services:ruby22:
-  stage: test
-  image: ruby:2.2
-  only:
-  - master
-  script:
-    - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services
-  cache:
-    key: "ruby22"
+    - bundle exec rake assets:precompile 2>/dev/null
+    - JOB_NAME=( $CI_BUILD_NAME )
+    - export CI_NODE_INDEX=${JOB_NAME[1]}
+    - export CI_NODE_TOTAL=${JOB_NAME[2]}
+    - export KNAPSACK_REPORT_PATH=knapsack/spinach_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
+    - export KNAPSACK_GENERATE_REPORT=true
+    - cp knapsack/spinach_report.json ${KNAPSACK_REPORT_PATH}
+    - knapsack spinach "-r rerun" || retry '[ ! -e tmp/spinach-rerun.txt ] || bundle exec spinach -r rerun $(cat tmp/spinach-rerun.txt)'
+  artifacts:
     paths:
-    - vendor
-
-spec:other:ruby22:
-  stage: test
-  image: ruby:2.2
+    - knapsack/
+
+rspec 0 20: *rspec-knapsack
+rspec 1 20: *rspec-knapsack
+rspec 2 20: *rspec-knapsack
+rspec 3 20: *rspec-knapsack
+rspec 4 20: *rspec-knapsack
+rspec 5 20: *rspec-knapsack
+rspec 6 20: *rspec-knapsack
+rspec 7 20: *rspec-knapsack
+rspec 8 20: *rspec-knapsack
+rspec 9 20: *rspec-knapsack
+rspec 10 20: *rspec-knapsack
+rspec 11 20: *rspec-knapsack
+rspec 12 20: *rspec-knapsack
+rspec 13 20: *rspec-knapsack
+rspec 14 20: *rspec-knapsack
+rspec 15 20: *rspec-knapsack
+rspec 16 20: *rspec-knapsack
+rspec 17 20: *rspec-knapsack
+rspec 18 20: *rspec-knapsack
+rspec 19 20: *rspec-knapsack
+
+spinach 0 10: *spinach-knapsack
+spinach 1 10: *spinach-knapsack
+spinach 2 10: *spinach-knapsack
+spinach 3 10: *spinach-knapsack
+spinach 4 10: *spinach-knapsack
+spinach 5 10: *spinach-knapsack
+spinach 6 10: *spinach-knapsack
+spinach 7 10: *spinach-knapsack
+spinach 8 10: *spinach-knapsack
+spinach 9 10: *spinach-knapsack
+
+# Execute all testing suites against Ruby 2.2
+
+.ruby-22: &ruby-22
+  image: "ruby:2.2"
   only:
-  - master
-  script:
-    - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:other
+    - master
   cache:
     key: "ruby22"
     paths:
     - vendor
 
-spinach:project:half:ruby22:
-  stage: test
-  image: ruby:2.2
-  only:
-  - master
-  script:
-    - RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
-    - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half
-  cache:
-    key: "ruby22"
-    paths:
-    - vendor
+.rspec-knapsack-ruby22: &rspec-knapsack-ruby22
+  <<: *rspec-knapsack
+  <<: *ruby-22
+
+.spinach-knapsack-ruby22: &spinach-knapsack-ruby22
+  <<: *spinach-knapsack
+  <<: *ruby-22
+  
+rspec 0 20 ruby22: *rspec-knapsack-ruby22
+rspec 1 20 ruby22: *rspec-knapsack-ruby22
+rspec 2 20 ruby22: *rspec-knapsack-ruby22
+rspec 3 20 ruby22: *rspec-knapsack-ruby22
+rspec 4 20 ruby22: *rspec-knapsack-ruby22
+rspec 5 20 ruby22: *rspec-knapsack-ruby22
+rspec 6 20 ruby22: *rspec-knapsack-ruby22
+rspec 7 20 ruby22: *rspec-knapsack-ruby22
+rspec 8 20 ruby22: *rspec-knapsack-ruby22
+rspec 9 20 ruby22: *rspec-knapsack-ruby22
+rspec 10 20 ruby22: *rspec-knapsack-ruby22
+rspec 11 20 ruby22: *rspec-knapsack-ruby22
+rspec 12 20 ruby22: *rspec-knapsack-ruby22
+rspec 13 20 ruby22: *rspec-knapsack-ruby22
+rspec 14 20 ruby22: *rspec-knapsack-ruby22
+rspec 15 20 ruby22: *rspec-knapsack-ruby22
+rspec 16 20 ruby22: *rspec-knapsack-ruby22
+rspec 17 20 ruby22: *rspec-knapsack-ruby22
+rspec 18 20 ruby22: *rspec-knapsack-ruby22
+rspec 19 20 ruby22: *rspec-knapsack-ruby22
+
+spinach 0 10 ruby22: *spinach-knapsack-ruby22
+spinach 1 10 ruby22: *spinach-knapsack-ruby22
+spinach 2 10 ruby22: *spinach-knapsack-ruby22
+spinach 3 10 ruby22: *spinach-knapsack-ruby22
+spinach 4 10 ruby22: *spinach-knapsack-ruby22
+spinach 5 10 ruby22: *spinach-knapsack-ruby22
+spinach 6 10 ruby22: *spinach-knapsack-ruby22
+spinach 7 10 ruby22: *spinach-knapsack-ruby22
+spinach 8 10 ruby22: *spinach-knapsack-ruby22
+spinach 9 10 ruby22: *spinach-knapsack-ruby22
+
+# Other generic tests
+
+.exec: &exec
+  stage: test
+  script:
+    - bundle exec $CI_BUILD_NAME
+
+teaspoon: *exec
+rubocop: *exec
+rake scss_lint: *exec
+rake brakeman: *exec
+rake flog: *exec
+rake flay: *exec
+rake db:migrate:reset: *exec
+license_finder: *exec
 
-spinach:project:rest:ruby22:
+bundler:audit:
   stage: test
-  image: ruby:2.2
   only:
-  - master
+    - master
   script:
-    - RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
-    - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest
-  cache:
-    key: "ruby22"
-    paths:
-    - vendor
+    - "bundle exec bundle-audit check --update --ignore OSVDB-115941"
 
-spinach:other:ruby22:
-  stage: test
-  image: ruby:2.2
-  only:
-  - master
-  script:
-    - RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
-    - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other
-  cache:
-    key: "ruby22"
-    paths:
-    - vendor
+# Notify slack in the end
 
 notify:slack:
-  stage: notifications
+  stage: post-test
   script:
     - ./scripts/notify_slack.sh "#builds" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>"
   when: on_failure
diff --git a/.rubocop.yml b/.rubocop.yml
index b95b006685a5ee303b3af3ed91f259868c14f6da..c97c9f68b5175ee56f16e871e848de7ddc56231c 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1,3 +1,5 @@
+require: rubocop-rspec
+
 AllCops:
   TargetRubyVersion: 2.1
   # Cop names are not displayed in offense messages by default. Change behavior
@@ -11,7 +13,8 @@ AllCops:
   # Exclude some GitLab files
   Exclude:
     - 'vendor/**/*'
-    - 'db/**/*'
+    - 'db/*'
+    - 'db/fixtures/**/*'
     - 'tmp/**/*'
     - 'bin/**/*'
     - 'lib/backup/**/*'
@@ -57,7 +60,7 @@ Style/AndOr:
 
 # Use `Array#join` instead of `Array#*`.
 Style/ArrayJoin:
-  Enabled: false
+  Enabled: true
 
 # Use only ascii symbols in comments.
 Style/AsciiComments:
@@ -69,7 +72,7 @@ Style/AsciiIdentifiers:
 
 # Checks for uses of Module#attr.
 Style/Attr:
-  Enabled: false
+  Enabled: true
 
 # Avoid the use of BEGIN blocks.
 Style/BeginBlock:
@@ -81,7 +84,7 @@ Style/BarePercentLiterals:
 
 # Do not use block comments.
 Style/BlockComments:
-  Enabled: false
+  Enabled: true
 
 # Put end statement of multiline block on its own line.
 Style/BlockEndNewline:
@@ -122,7 +125,7 @@ Style/ClassCheck:
 
 # Use self when defining module/class methods.
 Style/ClassMethods:
-  Enabled: false
+  Enabled: true
 
 # Avoid the use of class variables.
 Style/ClassVars:
@@ -152,7 +155,7 @@ Style/ConstantName:
 
 # Use def with parentheses when there are arguments.
 Style/DefWithParentheses:
-  Enabled: false
+  Enabled: true
 
 # Checks for use of deprecated Hash methods.
 Style/DeprecatedHashMethods:
@@ -192,7 +195,7 @@ Style/EmptyLines:
 
 # Keep blank lines around access modifiers.
 Style/EmptyLinesAroundAccessModifier:
-  Enabled: false
+  Enabled: true
 
 # Keeps track of empty lines around block bodies.
 Style/EmptyLinesAroundBlockBody:
@@ -216,15 +219,15 @@ Style/EmptyLiteral:
 
 # Avoid the use of END blocks.
 Style/EndBlock:
-  Enabled: false
+  Enabled: true
 
 # Use Unix-style line endings.
 Style/EndOfLine:
-  Enabled: false
+  Enabled: true
 
 # Favor the use of Fixnum#even? && Fixnum#odd?
 Style/EvenOdd:
-  Enabled: false
+  Enabled: true
 
 # Do not use unnecessary spacing.
 Style/ExtraSpacing:
@@ -232,15 +235,20 @@ Style/ExtraSpacing:
 
 # Use snake_case for source file names.
 Style/FileName:
-  Enabled: false
+  Enabled: true
+
+# Checks for a line break before the first parameter in a multi-line method
+# parameter definition.
+Style/FirstMethodParameterLineBreak:
+  Enabled: true
 
 # Checks for flip flops.
 Style/FlipFlop:
-  Enabled: false
+  Enabled: true
 
 # Checks use of for or each in multiline loops.
 Style/For:
-  Enabled: false
+  Enabled: true
 
 # Enforce the use of Kernel#sprintf, Kernel#format or String#%.
 Style/FormatString:
@@ -248,7 +256,7 @@ Style/FormatString:
 
 # Do not introduce global variables.
 Style/GlobalVars:
-  Enabled: false
+  Enabled: true
 
 # Check for conditionals that can be replaced with guard clauses.
 Style/GuardClause:
@@ -269,7 +277,7 @@ Style/IfUnlessModifier:
 
 # Do not use if x; .... Use the ternary operator instead.
 Style/IfWithSemicolon:
-  Enabled: false
+  Enabled: true
 
 # Checks that conditional statements do not have an identical line at the
 # end of each branch, which can validly be moved out of the conditional.
@@ -279,7 +287,7 @@ Style/IdenticalConditionalBranches:
 # Checks the indentation of the first line of the right-hand-side of a
 # multi-line assignment.
 Style/IndentAssignment:
-  Enabled: false
+  Enabled: true
 
 # Keep indentation straight.
 Style/IndentationConsistency:
@@ -299,7 +307,7 @@ Style/IndentHash:
 
 # Use Kernel#loop for infinite loops.
 Style/InfiniteLoop:
-  Enabled: false
+  Enabled: true
 
 # Use the new lambda literal syntax for single-line blocks.
 Style/Lambda:
@@ -307,11 +315,11 @@ Style/Lambda:
 
 # Use lambda.call(...) instead of lambda.(...).
 Style/LambdaCall:
-  Enabled: false
+  Enabled: true
 
 # Comments should start with a space.
 Style/LeadingCommentSpace:
-  Enabled: false
+  Enabled: true
 
 # Use \ instead of + or << to concatenate two string literals at line end.
 Style/LineEndConcatenation:
@@ -323,33 +331,56 @@ Style/MethodCallParentheses:
 
 # Checks if the method definitions have or don't have parentheses.
 Style/MethodDefParentheses:
-  Enabled: false
+  Enabled: true
 
 # Use the configured style when naming methods.
 Style/MethodName:
-  Enabled: false
+  Enabled: true
 
 # Checks for usage of `extend self` in modules.
 Style/ModuleFunction:
   Enabled: false
 
+# Checks that the closing brace in an array literal is either on the same line
+# as the last array element, or a new line.
+Style/MultilineArrayBraceLayout:
+  Enabled: false
+  EnforcedStyle: symmetrical
+
 # Avoid multi-line chains of blocks.
 Style/MultilineBlockChain:
-  Enabled: false
+  Enabled: true
 
 # Ensures newlines after multiline block do statements.
 Style/MultilineBlockLayout:
   Enabled: true
 
+# Checks that the closing brace in a hash literal is either on the same line as
+# the last hash element, or a new line.
+Style/MultilineHashBraceLayout:
+  Enabled: false
+  EnforcedStyle: symmetrical
+
 # Do not use then for multi-line if/unless.
 Style/MultilineIfThen:
+  Enabled: true
+
+# Checks that the closing brace in a method call is either on the same line as
+# the last method argument, or a new line.
+Style/MultilineMethodCallBraceLayout:
   Enabled: false
+  EnforcedStyle: symmetrical
 
 # Checks indentation of method calls with the dot operator that span more than
 # one line.
 Style/MultilineMethodCallIndentation:
   Enabled: false
 
+# Checks that the closing brace in a method definition is symmetrical with
+# respect to the opening brace and the method parameters.
+Style/MultilineMethodDefinitionBraceLayout:
+  Enabled: false
+
 # Checks indentation of binary operations that span more than one line.
 Style/MultilineOperationIndentation:
   Enabled: false
@@ -364,7 +395,7 @@ Style/MutableConstant:
 
 # Favor unless over if for negative conditions (or control flow or).
 Style/NegatedIf:
-  Enabled: false
+  Enabled: true
 
 # Favor until over while for negative conditions.
 Style/NegatedWhile:
@@ -372,7 +403,7 @@ Style/NegatedWhile:
 
 # Avoid using nested modifiers.
 Style/NestedModifier:
-  Enabled: false
+  Enabled: true
 
 # Parenthesize method calls which are nested inside the argument list of
 # another parenthesized method call.
@@ -409,7 +440,7 @@ Style/OneLineConditional:
 
 # When defining binary operators, name the argument other.
 Style/OpMethod:
-  Enabled: false
+  Enabled: true
 
 # Check for simple usages of parallel assignment. It will only warn when
 # the number of variables matches on both sides of the assignment.
@@ -456,10 +487,9 @@ Style/RedundantException:
 Style/RedundantFreeze:
   Enabled: false
 
-# TODO: Enable RedundantParentheses Cop.
 # Checks for parentheses that seem not to serve any purpose.
 Style/RedundantParentheses:
-  Enabled: false
+  Enabled: true
 
 # Don't use return where it's not required.
 Style/RedundantReturn:
@@ -485,11 +515,12 @@ Style/SelfAssignment:
 
 # Don't use semicolons to terminate expressions.
 Style/Semicolon:
-  Enabled: false
+  Enabled: true
 
 # Checks for proper usage of fail and raise.
 Style/SignalException:
-  Enabled: false
+  EnforcedStyle: only_raise
+  Enabled: true
 
 # Enforces the names of some block params.
 Style/SingleLineBlockParams:
@@ -510,25 +541,24 @@ Style/SpaceAfterComma:
 # Do not put a space between a method name and the opening parenthesis in a
 # method definition.
 Style/SpaceAfterMethodName:
-  Enabled: false
+  Enabled: true
 
 # Tracks redundant space after the ! operator.
 Style/SpaceAfterNot:
-  Enabled: false
+  Enabled: true
 
 # Use spaces after semicolons.
 Style/SpaceAfterSemicolon:
-  Enabled: false
+  Enabled: true
 
 # Checks that the equals signs in parameter default assignments have or don't
 # have surrounding space depending on configuration.
 Style/SpaceAroundEqualsInParameterDefault:
   Enabled: false
 
-# TODO: Enable SpaceAroundKeyword Cop.
 # Use a space around keywords if appropriate.
 Style/SpaceAroundKeyword:
-  Enabled: false
+  Enabled: true
 
 # Use a single space around operators.
 Style/SpaceAroundOperators:
@@ -540,11 +570,11 @@ Style/SpaceBeforeBlockBraces:
 
 # No spaces before commas.
 Style/SpaceBeforeComma:
-  Enabled: false
+  Enabled: true
 
 # Checks for missing space between code and a comment on the same line.
 Style/SpaceBeforeComment:
-  Enabled: false
+  Enabled: true
 
 # Checks that exactly one space is used between a method name and the first
 # argument for method calls without parentheses.
@@ -553,7 +583,7 @@ Style/SpaceBeforeFirstArg:
 
 # No spaces before semicolons.
 Style/SpaceBeforeSemicolon:
-  Enabled: false
+  Enabled: true
 
 # Checks that block braces have or don't have surrounding space.
 # For blocks taking parameters, checks that the left brace has or doesn't
@@ -575,11 +605,12 @@ Style/SpaceInsideParens:
 
 # No spaces inside range literals.
 Style/SpaceInsideRangeLiteral:
-  Enabled: false
+  Enabled: true
 
 # Checks for padding/surrounding spaces inside string interpolation.
 Style/SpaceInsideStringInterpolation:
-  Enabled: false
+  EnforcedStyle: no_space
+  Enabled: true
 
 # Avoid Perl-style global variables.
 Style/SpecialGlobalVars:
@@ -587,7 +618,8 @@ Style/SpecialGlobalVars:
 
 # Check for the usage of parentheses around stabby lambda arguments.
 Style/StabbyLambdaParentheses:
-  Enabled: false
+  EnforcedStyle: require_parentheses
+  Enabled: true
 
 # Checks if uses of quotes match the configured preference.
 Style/StringLiterals:
@@ -600,7 +632,9 @@ Style/StringLiteralsInInterpolation:
 
 # Checks if configured preferred methods are used over non-preferred.
 Style/StringMethods:
-  Enabled: false
+  PreferredMethods:
+    intern: to_sym
+  Enabled: true
 
 # Use %i or %I for arrays of symbols.
 Style/SymbolArray:
@@ -658,23 +692,24 @@ Style/UnneededPercentQ:
 
 # Don't interpolate global, instance and class variables directly in strings.
 Style/VariableInterpolation:
-  Enabled: false
+  Enabled: true
 
 # Use the configured style when naming variables.
 Style/VariableName:
-  Enabled: false
+  EnforcedStyle: snake_case
+  Enabled: true
 
 # Use when x then ... for one-line cases.
 Style/WhenThen:
-  Enabled: false
+  Enabled: true
 
 # Checks for redundant do after while or until.
 Style/WhileUntilDo:
-  Enabled: false
+  Enabled: true
 
 # Favor modifier while/until usage when you have a single-line body.
 Style/WhileUntilModifier:
-  Enabled: false
+  Enabled: true
 
 # Use %w or %W for arrays of words.
 Style/WordArray:
@@ -737,7 +772,7 @@ Metrics/PerceivedComplexity:
 # Checks for ambiguous operators in the first argument of a method invocation
 # without parentheses.
 Lint/AmbiguousOperator:
-  Enabled: false
+  Enabled: true
 
 # Checks for ambiguous regexp literals in the first argument of a method
 # invocation without parentheses.
@@ -750,7 +785,7 @@ Lint/AssignmentInCondition:
 
 # Align block ends correctly.
 Lint/BlockAlignment:
-  Enabled: false
+  Enabled: true
 
 # Default values in optional keyword arguments and optional ordinal arguments
 # should not refer back to the name of the argument.
@@ -843,7 +878,7 @@ Lint/InvalidCharacterLiteral:
 
 # Checks of literals used in conditions.
 Lint/LiteralInCondition:
-  Enabled: false
+  Enabled: true
 
 # Checks for literals used in interpolation.
 Lint/LiteralInInterpolation:
@@ -955,10 +990,9 @@ Performance/EndWith:
 Performance/LstripRstrip:
   Enabled: true
 
-# TODO: Enable RangeInclude Cop.
 # Use `Range#cover?` instead of `Range#include?`.
 Performance/RangeInclude:
-  Enabled: false
+  Enabled: true
 
 # TODO: Enable RedundantBlockCall Cop.
 # Use `yield` instead of `block.call`.
@@ -982,11 +1016,10 @@ Performance/RedundantMerge:
 Performance/RedundantSortBy:
   Enabled: true
 
-# TODO: Enable StartWith Cop.
 # Use `start_with?` instead of a regex match anchored to the beginning of a
 # string.
 Performance/StartWith:
-  Enabled: false
+  Enabled: true
 
 # Use `tr` instead of `gsub` when you are replacing the same number of
 # characters. Use `delete` instead of `gsub` when you are deleting
@@ -994,10 +1027,9 @@ Performance/StartWith:
 Performance/StringReplacement:
   Enabled: true
 
-# TODO: Enable TimesMap Cop.
 # Checks for `.times.map` calls.
 Performance/TimesMap:
-  Enabled: false
+  Enabled: true
 
 
 ##################### Rails ##################################
@@ -1006,6 +1038,9 @@ Performance/TimesMap:
 Rails:
   Enabled: true
 
+Rails/UniqBeforePluck:
+  Enabled: false
+
 # Enforces consistent use of action filter methods.
 Rails/ActionFilter:
   Enabled: true
@@ -1056,3 +1091,68 @@ Rails/TimeZone:
 # Use validates :attribute, hash of validations.
 Rails/Validation:
   Enabled: false
+
+Rails/UniqBeforePluck:
+  Enabled: false
+
+##################### RSpec ##################################
+
+# Check that instances are not being stubbed globally.
+RSpec/AnyInstance:
+  Enabled: false
+
+# Check that the first argument to the top level describe is the tested class or
+# module.
+RSpec/DescribeClass:
+  Enabled: false
+
+# Use `described_class` for tested class / module.
+RSpec/DescribeMethod:
+  Enabled: false
+
+# Checks that the second argument to top level describe is the tested method
+# name.
+RSpec/DescribedClass:
+  Enabled: false
+
+# Checks for long example.
+RSpec/ExampleLength:
+  Enabled: false
+  Max: 5
+
+# Do not use should when describing your tests.
+RSpec/ExampleWording:
+  Enabled: false
+  CustomTransform:
+    be: is
+    have: has
+    not: does not
+  IgnoredWords: []
+
+# Checks the file and folder naming of the spec file.
+RSpec/FilePath:
+  Enabled: false
+  CustomTransform:
+    RuboCop: rubocop
+    RSpec: rspec
+
+# Checks if there are focused specs.
+RSpec/Focus:
+  Enabled: true
+
+# Checks for the usage of instance variables.
+RSpec/InstanceVariable:
+  Enabled: false
+
+# Checks for multiple top-level describes.
+RSpec/MultipleDescribes:
+  Enabled: false
+
+# Enforces the usage of the same method on all negative message expectations.
+RSpec/NotToNot:
+  EnforcedStyle: not_to
+  Enabled: true
+
+# Prefer using verifying doubles over normal doubles.
+RSpec/VerifiedDoubles:
+  Enabled: false
diff --git a/.vagrant_enabled b/.vagrant_enabled
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/CHANGELOG b/CHANGELOG
index 61eb3030c8711dcece4730ef468ecf3e104d89cf..bad4b0423bae44107c3cbb09cfe7be756eed61c2 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,8 +1,162 @@
 Please view this file on the master branch, on stable branches it's out of date.
 
+v 8.9.0 (unreleased)
+  - Fix Error 500 when using closes_issues API with an external issue tracker
+  - Add more information into RSS feed for issues (Alexander Matyushentsev)
+  - Bulk assign/unassign labels to issues.
+  - Ability to prioritize labels !4009 / !3205 (Thijs Wouters)
+  - Fix endless redirections when accessing user OAuth applications when they are disabled
+  - Allow enabling wiki page events from Webhook management UI
+  - Bump rouge to 1.11.0
+  - Fix issue with arrow keys not working in search autocomplete dropdown
+  - Fix an issue where note polling stopped working if a window was in the
+    background during a refresh.
+  - Make EmailsOnPushWorker use Sidekiq mailers queue
+  - Fix wiki page events' webhook to point to the wiki repository
+  - Don't show tags for revert and cherry-pick operations
+  - Fix issue todo not remove when leave project !4150 (Long Nguyen)
+  - Allow customisable text on the 'nearly there' page after a user signs up
+  - Bump recaptcha gem to 3.0.0 to remove deprecated stoken support
+  - Fix SVG sanitizer to allow more elements
+  - Allow forking projects with restricted visibility level
+  - Added descriptions to notification settings dropdown
+  - Improve note validation to prevent errors when creating invalid note via API
+  - Reduce number of fog gem dependencies
+  - Implement a fair usage of shared runners
+  - Remove project notification settings associated with deleted projects
+  - Fix 404 page when viewing TODOs that contain milestones or labels in different projects
+  - Add a metric for the number of new Redis connections created by a transaction
+  - Fix Error 500 when viewing a blob with binary characters after the 1024-byte mark
+  - Redesign navigation for project pages
+  - Added shortcut 'y' for copying a files content hash URL #14470
+  - Fix groups API to list only user's accessible projects
+  - Fix horizontal scrollbar for long commit message.
+  - Add Environments and Deployments
+  - Redesign account and email confirmation emails
+  - Don't fail builds for projects that are deleted
+  - Support Docker Registry manifest v1
+  - `git clone https://host/namespace/project` now works, in addition to using the `.git` suffix
+  - Bump nokogiri to 1.6.8
+  - Use gitlab-shell v3.0.0
+  - Upgrade to jQuery 2
+  - Use Knapsack to evenly distribute tests across multiple nodes
+  - Add `sha` parameter to MR merge API, to ensure only reviewed changes are merged
+  - Don't allow MRs to be merged when commits were added since the last review / page load
+  - Add DB index on users.state
+  - Add rake task 'gitlab:db:configure' for conditionally seeding or migrating the database
+  - Changed the Slack build message to use the singular duration if necessary (Aran Koning)
+  - Fix race condition on merge when build succeeds
+  - Links from a wiki page to other wiki pages should be rewritten as expected
+  - Add option to project to only allow merge requests to be merged if the build succeeds (Rui Santos)
+  - Added navigation shortcuts to the project pipelines, milestones, builds and forks page. !4393
+  - Fix issues filter when ordering by milestone
+  - Added artifacts:when to .gitlab-ci.yml - this requires GitLab Runner 1.3
+  - Bamboo Service: Fix missing credentials & URL handling when base URL contains a path (Benjamin Schmid)
+  - TeamCity Service: Fix URL handling when base URL contains a path
+  - Todos will display target state if issuable target is 'Closed' or 'Merged'
+  - Fix bug when sorting issues by milestone due date and filtering by two or more labels
+  - Add support for using Yubikeys (U2F) for two-factor authentication
+  - Link to blank group icon doesn't throw a 404 anymore
+  - Remove 'main language' feature
+  - Toggle whitespace button now available for compare branches diffs #17881
+  - Pipelines can be canceled only when there are running builds
+  - Use downcased path to container repository as this is expected path by Docker
+  - Projects pending deletion will render a 404 page
+  - Measure queue duration between gitlab-workhorse and Rails
+  - Make Omniauth providers specs to not modify global configuration
+  - Make authentication service for Container Registry to be compatible with < Docker 1.11
+  - Add Application Setting to configure Container Registry token expire delay (default 5min)
+  - Cache assigned issue and merge request counts in sidebar nav
+  - Use Knapsack only in CI environment
+  - Cache project build count in sidebar nav
+  - Add milestone expire date to the right sidebar
+  - Manually mark a issue or merge request as a todo
+  - Fix markdown_spec to use before instead of before(:all) to properly cleanup database after testing
+  - Reduce number of queries needed to render issue labels in the sidebar
+  - Improve error handling importing projects
+  - Remove duplicated notification settings
+  - Put project Files and Commits tabs under Code tab
+  - Decouple global notification level from user model
+  - Replace Colorize with Rainbow for coloring console output in Rake tasks.
+  - Add workhorse controller and API helpers
+  - An indicator is now displayed at the top of the comment field for confidential issues.
+  - Show categorised search queries in the search autocomplete
+  - RepositoryCheck::SingleRepositoryWorker public and private methods are now instrumented
+  - Improve issuables APIs performance when accessing notes !4471
+  - External links now open in a new tab
+  - Prevent default actions of disabled buttons and links
+  - Markdown editor now correctly resets the input value on edit cancellation !4175
+  - Toggling a task list item in a issue/mr description does not creates a Todo for mentions
+  - Improved UX of date pickers on issue & milestone forms
+  - Cache on the database if a project has an active external issue tracker.
+  - Put project Labels and Milestones pages links under Issues and Merge Requests tabs as subnav
+  - All classes in the Banzai::ReferenceParser namespace are now instrumented
+  - Remove deprecated issues_tracker and issues_tracker_id from project model
+  - Allow users to create confidential issues in private projects
+  - Measure CPU time for instrumented methods
+  - Instrument private methods and private instance methods by default instead just public methods
+  - Only show notes through JSON on confidential issues that the user has access to
+  - Updated the allocations Gem to version 1.0.5
+  - The background sampler now ignores classes without names
+  - Update design for `Close` buttons
+  - New custom icons for navigation
+  - Horizontally scrolling navigation on project, group, and profile settings pages
+  - Hide global side navigation by default
+  - Fix project Star/Unstar project button tooltip
+  - Remove tanuki logo from side navigation; center on top nav
+  - Include user relationships when retrieving award_emoji
+  - Various associations are now eager loaded when parsing issue references to reduce the number of queries executed
+  - Set inverse_of for Project/Service association to reduce the number of queries
+
+v 8.8.5
+  - Import GitHub repositories respecting the API rate limit !4166
+  - Fix todos page throwing errors when you have a project pending deletion !4300
+  - Disable Webhooks before proceeding with the GitHub import !4470
+  - Fix importer for GitHub comments on diff !4488
+  - Adjust the SAML control flow to allow LDAP identities to be added to an existing SAML user !4498
+  - Fix incremental trace upload API when using multi-byte UTF-8 chars in trace !4541
+  - Prevent unauthorized access for projects build traces
+  - Forbid scripting for wiki files
+  - Only show notes through JSON on confidential issues that the user has access to
+
+v 8.8.4
+  - Fix LDAP-based login for users with 2FA enabled. !4493
+  - Added descriptions to notification settings dropdown
+  - Due date can be removed from milestones
+
+v 8.8.3
+  - Fix 404 page when viewing TODOs that contain milestones or labels in different projects. !4312
+  - Fixed JS error when trying to remove discussion form. !4303
+  - Fixed issue with button color when no CI enabled. !4287
+  - Fixed potential issue with 2 CI status polling events happening. !3869
+  - Improve design of Pipeline view. !4230
+  - Fix gitlab importer failing to import new projects due to missing credentials. !4301
+  - Fix import URL migration not rescuing with the correct Error. !4321
+  - Fix health check access token changing due to old application settings being used. !4332
+  - Make authentication service for Container Registry to be compatible with Docker versions before 1.11. !4363
+  - Add Application Setting to configure Container Registry token expire delay (default 5 min). !4364
+  - Pass the "Remember me" value to the 2FA token form. !4369
+  - Fix incorrect links on pipeline page when merge request created from fork.  !4376
+  - Use downcased path to container repository as this is expected path by Docker. !4420
+  - Fix wiki project clone address error (chujinjin). !4429
+  - Fix serious performance bug with rendering Markdown with InlineDiffFilter.  !4392
+  - Fix missing number on generated ordered list element. !4437
+  - Prevent disclosure of notes on confidential issues in search results.
+
+v 8.8.2
+  - Added remove due date button. !4209
+  - Fix Error 500 when accessing application settings due to nil disabled OAuth sign-in sources. !4242
+  - Fix Error 500 in CI charts by gracefully handling commits with no durations. !4245
+  - Fix table UI on CI builds page. !4249
+  - Fix backups if registry is disabled. !4263
+  - Fixed issue with merge button color. !4211
+  - Fixed issue with enter key selecting wrong option in dropdown. !4210
+  - When creating a .gitignore file a dropdown with templates will be provided. !4075
+  - Fix concurrent request when updating build log in browser. !4183
+
 v 8.8.1
   - Add documentation for the "Health Check" feature
-  - Allow anonymous users to access a public project's pipelines
+  - Allow anonymous users to access a public project's pipelines !4233
   - Fix MySQL compatibility in zero downtime migrations helpers
   - Fix the CI login to Container Registry (the gitlab-ci-token user)
 
@@ -19,6 +173,7 @@ v 8.8.0
   - Added inline diff styling for `change_title` system notes. (Adam Butler)
   - Project#open_branches has been cleaned up and no longer loads entire records into memory.
   - Escape HTML in commit titles in system note messages
+  - Improve design of Pipeline View
   - Fix scope used when accessing container registry
   - Fix creation of Ci::Commit object which can lead to pending, failed in some scenarios
   - Improve multiple branch push performance by memoizing permission checking
@@ -78,11 +233,21 @@ v 8.8.0
   - Improve Issue formatting for the Slack Service (Jeroen van Baarsen)
   - Fixed advice on invalid permissions on upload path !2948 (Ludovic Perrine)
   - Allows MR authors to have the source branch removed when merging the MR. !2801 (Jeroen Jacobs)
+  - When creating a .gitignore file a dropdown with templates will be provided
+  - Shows the issue/MR list search/filter form and corrects the mobile styling for guest users. #17562
+
+v 8.7.7
+  - Fix import by `Any Git URL` broken if the URL contains a space
+  - Prevent unauthorized access to other projects build traces
+  - Forbid scripting for wiki files
+  - Only show notes through JSON on confidential issues that the user has access to
 
 v 8.7.6
   - Fix links on wiki pages for relative url setups. !4131 (Artem Sidorenko)
   - Fix import from GitLab.com to a private instance failure. !4181
   - Fix external imports not finding the import data. !4106
+  - Fix notification delay when changing status of an issue
+  - Bump Workhorse to 0.7.5 so it can serve raw diffs
 
 v 8.7.5
   - Fix relative links in wiki pages. !4050
@@ -242,6 +407,11 @@ v 8.7.0
   - Add RAW build trace output and button on build page
   - Add incremental build trace update into CI API
 
+v 8.6.9
+  - Prevent unauthorized access to other projects build traces
+  - Forbid scripting for wiki files
+  - Only show notes through JSON on confidential issues that the user has access to
+
 v 8.6.8
   - Prevent privilege escalation via "impersonate" feature
   - Prevent privilege escalation via notes API
@@ -396,6 +566,10 @@ v 8.6.0
   - Trigger a todo for mentions on commits page
   - Let project owners and admins soft delete issues and merge requests
 
+v 8.5.13
+  - Prevent unauthorized access to other projects build traces
+  - Forbid scripting for wiki files
+
 v 8.5.12
   - Prevent privilege escalation via "impersonate" feature
   - Prevent privilege escalation via notes API
@@ -575,6 +749,10 @@ v 8.5.0
   - Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul)
   - Add Todos
 
+v 8.4.11
+  - Prevent unauthorized access to other projects build traces
+  - Forbid scripting for wiki files
+
 v 8.4.10
   - Prevent privilege escalation via "impersonate" feature
   - Prevent privilege escalation via notes API
@@ -717,6 +895,10 @@ v 8.4.0
   - 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.10
+  - Prevent unauthorized access to other projects build traces
+  - Forbid scripting for wiki files
+
 v 8.3.9
   - Prevent privilege escalation via "impersonate" feature
   - Prevent privilege escalation via notes API
@@ -835,6 +1017,10 @@ v 8.3.0
   - Expose Git's version in the admin area
   - Show "New Merge Request" buttons on canonical repos when you have a fork (Josh Frye)
 
+v 8.2.6
+  - Prevent unauthorized access to other projects build traces
+  - Forbid scripting for wiki files
+
 v 8.2.5
   - Prevent privilege escalation via "impersonate" feature
   - Prevent privilege escalation via notes API
diff --git a/CHANGELOG-EE b/CHANGELOG-EE
index 3603f62566c50ab370344b3a40ad6a5f1b942665..2d83cff67cf1dd3ca157aab3443292eb51258653 100644
--- a/CHANGELOG-EE
+++ b/CHANGELOG-EE
@@ -1,10 +1,35 @@
 Please view this file on the master branch, on stable branches it's out of date.
 
 v 8.9.0 (unreleased)
+  - Fix JenkinsService test button
+  - Fix nil user handling in UpdateMirrorService
+  - Allow LDAP to mark users as external based on their group membership. !432
+  - Forbid MR authors from approving their own MRs
+  - Instrument instance methods of Gitlab::InsecureKeyFingerprint class
+  - Add API endpoint for Merge Request Approvals !449
+  - Distribute RepositoryUpdateMirror jobs in time and add exclusive lease on them by project_id
+  - [Elastic] Move ES settings to application settings
+  - Disable mirror flag for projects without import_url
+  - UpdateMirror service return an error status when no mirror
+  - Show flash notice when Git Hooks are updated successfully
+  - Remove explicit Gitlab::Metrics.action assignments, are already automatic.
+  - [Elastic] Project members with guest role can't access confidential issues
+  - Ability to lock file or folder in the repository
+  - Fix: Git hooks don't fire when committing from the UI
+
+v 8.8.5
+  - Make sure OAuth routes that we generate for Geo matches with the ones in Rails routes !444
+
+v 8.8.4
+  - Remove license overusage message
 
 v 8.8.3
-  - Make it clear the license overusage is visible only to admins
-  - Reduce load on DB for license upgrade check
+  - Add standard web hook headers to Jenkins CI post. !374
+  - Gracefully handle malformed DNs in LDAP group sync. !392
+  - Reduce load on DB for license upgrade check. !421
+  - Make it clear the license overusage message is visible only to admins. !423
+  - Fix Git hook validations for fast-forward merges. !427
+  - [Elastic] In search results, only show notes on confidential issues that the user has access to.
 
 v 8.8.2
   - Fix repository mirror updates for new imports stuck in started
@@ -32,6 +57,9 @@ v 8.8.0
   - API requests to /internal/authorized_keys are now tagged properly
   - Geo: Single Sign Out support !380
 
+v 8.7.7
+  - No EE-specific changes
+
 v 8.7.6
   - Bump GitLab Pages to 0.2.4 to fix Content-Type for predefined 404
 
@@ -60,6 +88,9 @@ v 8.7.0
   - Add ability to sync to remote mirrors. !249
   - GitLab Geo: Many replication improvements and fixes !354
 
+v 8.6.9
+  - No EE-specific changes
+
 v 8.6.8
   - No EE-specific changes
 
@@ -119,6 +150,9 @@ v 8.6.0
   - Allow SSL verification to be configurable when importing GitHub projects
   - Disable git-hooks for git annex commits
 
+v 8.5.13
+  - No EE-specific changes
+
 v 8.5.12
   - No EE-specific changes
 
@@ -174,6 +208,9 @@ v 8.5.0
   - Fix of Elastic indexer. Stabilze indexer when serialized data is corrupted
   - [Elastic] Don't index unnecessary data into elastic
 
+v 8.4.11
+  - No EE-specific changes
+
 v 8.4.10
   - No EE-specific changes
 
@@ -224,6 +261,9 @@ v 8.4.0
   - Add option to trigger builds when branches or tags are updated from a mirrored upstream repository
   - Ability to use Elasticsearch as a search engine
 
+v 8.3.10
+  - No EE-specific changes
+
 v 8.3.9
   - No EE-specific changes
 
@@ -261,6 +301,9 @@ v 8.3.0
   - Automatically import Kerberos identities from Active Directory when Kerberos is enabled (Alex Lossent)
   - Canonicalization of Kerberos identities to always include realm (Alex Lossent)
 
+v 8.2.6
+  - No EE-specific changes
+
 v 8.2.5
   - No EE-specific changes
 
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9fe4cf7b0f6d8ffc4ae9e2e5923daf8ceb32d7b1..f4472214778e6efcb8614145e4cbd6dc283331da 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -96,7 +96,7 @@ The designs are made using Antetype (`.atype` files). You can use the
 [free Antetype viewer (Mac OSX only)] or grab an exported PNG from the design
 (the PNG is 1:1).
 
-The current designs can be found in the [`gitlab1.atype` file].
+The current designs can be found in the [`gitlab8.atype` file].
 
 ### UI development kit
 
@@ -308,16 +308,14 @@ tests are least likely to receive timely feedback. The workflow to make a merge
 request is as follows:
 
 1. Fork the project into your personal space on GitLab.com
-1. Create a feature branch
+1. Create a feature branch, branch away from `master`.
 1. Write [tests](https://gitlab.com/gitlab-org/gitlab-development-kit#running-the-tests) and code
 1. Add your changes to the [CHANGELOG](CHANGELOG)
-1. If you are changing the README, some documentation or other things which
-   have no effect on the tests, add `[ci skip]` somewhere in the commit message
-   and make sure to read the [documentation styleguide][doc-styleguide]
+1. If you are writing documentation, make sure to read the [documentation styleguide][doc-styleguide]
 1. If you have multiple commits please combine them into one commit by
    [squashing them][git-squash]
 1. Push the commit(s) to your fork
-1. Submit a merge request (MR) to the master branch
+1. Submit a merge request (MR) to the `master` branch
 1. The MR title should describe the change you want to make
 1. The MR description should give a motive for your change and the method you
    used to achieve it, see the [merge request description format]
@@ -407,6 +405,7 @@ description area. Copy-paste it to retain the markdown format.
       entire line to follow it. This prevents linting tools from generating warnings.
     - Don't touch neighbouring lines. As an exception, automatic mass
       refactoring modifications may leave style non-compliant.
+1. If the merge request adds any new libraries (gems, JavaScript libraries, etc.), they should conform to our [Licensing guidelines][license-finder-doc]. See the instructions in that document for help if your MR fails the "license-finder" test with a "Dependencies that need approval" error.
 
 ## Changes for Stable Releases
 
@@ -532,4 +531,5 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
 [scss-styleguide]: doc/development/scss_styleguide.md "SCSS styleguide"
 [gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design
 [free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12
-[`gitlab1.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/gitlab1.atype/
+[`gitlab8.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/current/
+[license-finder-doc]: doc/development/licensing.md
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 37c2961c2430f357166156e7ddf1c590eb8d4ce1..4a36342fcab700951adb18ae7adc930997f6c3f4 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-2.7.2
+3.0.0
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index 39e898a4f952d339c155a7939d571a5fdd6c8cfc..8bd6ba8c5c366585c0343c027cb738d5fdeb8d4d 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-0.7.1
+0.7.5
diff --git a/Gemfile b/Gemfile
index 0b3cb4ee1eb7c3eb1095a1c7fe9228428feb3e3f..588c9b789e872b7ff1f30d457f6bb1ebce07e1eb 100644
--- a/Gemfile
+++ b/Gemfile
@@ -18,9 +18,8 @@ gem "mysql2", '~> 0.3.16', group: :mysql
 gem "pg", '~> 0.18.2', group: :postgres
 
 # Authentication libraries
-gem 'devise',                 '~> 3.5.4'
+gem 'devise',                 '~> 4.0'
 gem 'doorkeeper',             '~> 3.1'
-gem 'devise-async',           '~> 0.9.0'
 gem 'omniauth',               '~> 1.3.1'
 gem 'omniauth-auth0',         '~> 1.4.1'
 gem 'omniauth-azure-oauth2',  '~> 0.0.6'
@@ -40,19 +39,20 @@ gem 'rack-oauth2',            '~> 1.2.1'
 gem 'jwt'
 
 # Spam and anti-bot protection
-gem 'recaptcha', require: 'recaptcha/rails'
+gem 'recaptcha', '~> 3.0', require: 'recaptcha/rails'
 gem 'akismet', '~> 2.0'
 
 # Two-factor authentication
-gem 'devise-two-factor', '~> 2.0.0'
+gem 'devise-two-factor', '~> 3.0.0'
 gem 'rqrcode-rails3', '~> 0.1.7'
-gem 'attr_encrypted', '~> 1.3.4'
+gem 'attr_encrypted', '~> 3.0.0'
+gem 'u2f', '~> 0.2.1'
 
 # GitLab Pages
 gem 'validates_hostname', '~> 1.0.0'
 
 # Browser detection
-gem "browser", '~> 1.0.0'
+gem "browser", '~> 2.0.3'
 
 # Extracting information from a git repository
 # Provide access to Gitlab::Git library
@@ -78,7 +78,7 @@ gem 'grape-entity', '~> 0.4.2'
 gem 'rack-cors',    '~> 0.4.0', require: 'rack/cors'
 
 # Pagination
-gem "kaminari", "~> 0.16.3"
+gem "kaminari", "~> 0.17.0"
 
 # HAML
 gem "haml-rails", '~> 0.9.0'
@@ -89,8 +89,15 @@ gem "carrierwave", '~> 0.10.0'
 # Drag and Drop UI
 gem 'dropzonejs-rails', '~> 0.7.1'
 
+# for backups
+gem 'fog-aws', '~> 0.9'
+gem 'fog-azure', '~> 0.0'
+gem 'fog-core', '~> 1.40'
+gem 'fog-local', '~> 0.3'
+gem 'fog-google', '~> 0.3'
+gem 'fog-openstack', '~> 0.1'
+
 # for aws storage
-gem "fog", "~> 1.36.0"
 gem "unf", '~> 0.1.4'
 
 # Authorization
@@ -115,7 +122,7 @@ gem 'org-ruby',      '~> 0.9.12'
 gem 'creole',        '~> 0.5.0'
 gem 'wikicloth',     '0.8.1'
 gem 'asciidoctor',   '~> 1.5.2'
-gem 'rouge',         '~> 1.10.1'
+gem 'rouge',         '~> 1.11'
 
 # See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
 # and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
@@ -131,7 +138,7 @@ group :unicorn do
 end
 
 # State machine
-gem "state_machines-activerecord", '~> 0.3.0'
+gem "state_machines-activerecord", '~> 0.4.0'
 # Run events after state machine commits
 gem 'after_commit_queue'
 
@@ -148,7 +155,7 @@ gem 'redis-namespace'
 gem "httparty", '~> 0.13.3'
 
 # Colored output to console
-gem "colorize", '~> 0.7.0'
+gem "rainbow", '~> 2.1.0'
 
 # GitLab settings
 gem 'settingslogic', '~> 2.0.9'
@@ -188,9 +195,6 @@ gem 'ruby-fogbugz', '~> 0.2.1'
 # d3
 gem 'd3_rails', '~> 3.5.0'
 
-#cal-heatmap
-gem 'cal-heatmap-rails', '~> 3.6.0'
-
 # underscore-rails
 gem "underscore-rails", "~> 1.8.0"
 
@@ -216,6 +220,9 @@ gem 'mousetrap-rails', '~> 1.4.6'
 # Detect and convert string character encoding
 gem 'charlock_holmes', '~> 0.7.3'
 
+# Parse duration
+gem 'chronic_duration', '~> 0.10.6'
+
 gem "sass-rails", '~> 5.0.0'
 gem "coffee-rails", '~> 4.1.0'
 gem "uglifier", '~> 2.7.2'
@@ -230,7 +237,6 @@ gem 'gon',                '~> 6.0.1'
 gem 'jquery-atwho-rails', '~> 1.3.2'
 gem 'jquery-rails',       '~> 4.1.0'
 gem 'jquery-ui-rails',    '~> 5.0.0'
-gem 'raphael-rails',      '~> 2.1.2'
 gem 'request_store',      '~> 1.3.0'
 gem 'select2-rails',      '~> 3.5.9'
 gem 'virtus',             '~> 1.0.1'
@@ -252,7 +258,7 @@ end
 
 group :development do
   gem "foreman"
-  gem 'brakeman', '~> 3.2.0', require: false
+  gem 'brakeman', '~> 3.3.0', require: false
 
   gem 'letter_opener_web', '~> 1.3.0'
   gem 'quiet_assets', '~> 1.0.2'
@@ -304,15 +310,19 @@ group :development, :test do
   gem 'spring-commands-spinach',  '~> 1.1.0'
   gem 'spring-commands-teaspoon', '~> 0.0.2'
 
-  gem 'rubocop', '~> 0.38.0', require: false
+  gem 'rubocop', '~> 0.40.0', require: false
+  gem 'rubocop-rspec', '~> 1.5.0', require: false
   gem 'scss_lint', '~> 0.47.0', require: false
-  gem 'coveralls',  '~> 0.8.2', require: false
+  gem 'coveralls', '~> 0.8.2', require: false
   gem 'simplecov', '~> 0.11.0', require: false
   gem 'flog', require: false
   gem 'flay', require: false
   gem 'bundler-audit', require: false
 
   gem 'benchmark-ips', require: false
+
+  gem "license_finder", require: false
+  gem 'knapsack'
 end
 
 group :test do
@@ -336,7 +346,7 @@ gem "mail_room", "~> 0.7"
 gem 'email_reply_parser', '~> 0.5.8'
 
 ## CI
-gem 'activerecord-session_store', '~> 0.1.0'
+gem 'activerecord-session_store', '~> 1.0.0'
 gem "nested_form", '~> 0.3.2'
 
 # OAuth
diff --git a/Gemfile.lock b/Gemfile.lock
index 29f1416896004c255887a94e28e1e6c568b4c484..51663637d6b27abf3fee50c8a2b3997e7378eb20 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,7 +1,6 @@
 GEM
   remote: https://rubygems.org/
   specs:
-    CFPropertyList (2.3.2)
     RedCloth (4.2.9)
     ace-rails-ap (4.0.2)
     actionmailer (4.2.6)
@@ -33,10 +32,12 @@ GEM
       activemodel (= 4.2.6)
       activesupport (= 4.2.6)
       arel (~> 6.0)
-    activerecord-session_store (0.1.2)
-      actionpack (>= 4.0.0, < 5)
-      activerecord (>= 4.0.0, < 5)
-      railties (>= 4.0.0, < 5)
+    activerecord-session_store (1.0.0)
+      actionpack (>= 4.0, < 5.1)
+      activerecord (>= 4.0, < 5.1)
+      multi_json (~> 1.11, >= 1.11.2)
+      rack (>= 1.5.2, < 3)
+      railties (>= 4.0, < 5.1)
     activesupport (4.2.6)
       i18n (~> 0.7)
       json (~> 1.7, >= 1.7.7)
@@ -49,7 +50,7 @@ GEM
     after_commit_queue (1.3.0)
       activerecord (>= 3.0)
     akismet (2.0.0)
-    allocations (1.0.4)
+    allocations (1.0.5)
     arel (6.0.3)
     asana (0.4.0)
       faraday (~> 0.9)
@@ -58,8 +59,8 @@ GEM
       oauth2 (~> 1.0)
     asciidoctor (1.5.3)
     ast (2.2.0)
-    attr_encrypted (1.3.4)
-      encryptor (>= 1.3.0)
+    attr_encrypted (3.0.1)
+      encryptor (~> 3.0.0)
     attr_required (1.0.0)
     autoprefixer-rails (6.2.3)
       execjs
@@ -69,9 +70,24 @@ GEM
       descendants_tracker (~> 0.0.4)
       ice_nine (~> 0.11.0)
       thread_safe (~> 0.3, >= 0.3.1)
+    azure (0.7.5)
+      addressable (~> 2.3)
+      azure-core (~> 0.1)
+      faraday (~> 0.9)
+      faraday_middleware (~> 0.10)
+      json (~> 1.8)
+      mime-types (>= 1, < 3.0)
+      nokogiri (~> 1.6)
+      systemu (~> 2.6)
+      thor (~> 0.19)
+      uuid (~> 2.0)
+    azure-core (0.1.2)
+      faraday (~> 0.9)
+      faraday_middleware (~> 0.10)
+      nokogiri (~> 1.6)
     babosa (1.0.2)
     base32 (0.3.2)
-    bcrypt (3.1.10)
+    bcrypt (3.1.11)
     benchmark-ips (2.3.0)
     better_errors (1.0.1)
       coderay (>= 1.0.0)
@@ -81,17 +97,8 @@ GEM
     bootstrap-sass (3.3.6)
       autoprefixer-rails (>= 5.2.1)
       sass (>= 3.3.4)
-    brakeman (3.2.1)
-      erubis (~> 2.6)
-      haml (>= 3.0, < 5.0)
-      highline (>= 1.6.20, < 2.0)
-      ruby2ruby (~> 2.3.0)
-      ruby_parser (~> 3.8.1)
-      safe_yaml (>= 1.0)
-      sass (~> 3.0)
-      slim (>= 1.3.6, < 4.0)
-      terminal-table (~> 1.4)
-    browser (1.0.1)
+    brakeman (3.3.2)
+    browser (2.0.3)
     builder (3.2.2)
     bullet (5.0.0)
       activesupport (>= 3.0.0)
@@ -100,7 +107,6 @@ GEM
       bundler (~> 1.2)
       thor (~> 0.18)
     byebug (8.2.1)
-    cal-heatmap-rails (3.6.0)
     capybara (2.6.2)
       addressable
       mime-types (>= 1.16)
@@ -118,6 +124,8 @@ GEM
       mime-types (>= 1.16)
     cause (0.1)
     charlock_holmes (0.7.3)
+    chronic_duration (0.10.6)
+      numerizer (~> 0.1.1)
     chunky_png (1.3.5)
     cliver (0.3.2)
     coderay (1.1.0)
@@ -154,21 +162,18 @@ GEM
       activerecord (>= 3.2.0, < 5.0)
     descendants_tracker (0.0.4)
       thread_safe (~> 0.3, >= 0.3.1)
-    devise (3.5.4)
+    devise (4.1.1)
       bcrypt (~> 3.0)
       orm_adapter (~> 0.1)
-      railties (>= 3.2.6, < 5)
+      railties (>= 4.1.0, < 5.1)
       responders
-      thread_safe (~> 0.1)
       warden (~> 1.2.3)
-    devise-async (0.9.0)
-      devise (~> 3.2)
-    devise-two-factor (2.0.1)
+    devise-two-factor (3.0.0)
       activesupport
-      attr_encrypted (~> 1.3.2)
-      devise (~> 3.5.0)
+      attr_encrypted (>= 1.3, < 4, != 2)
+      devise (~> 4.0)
       railties
-      rotp (~> 2)
+      rotp (~> 2.0)
     diff-lcs (1.2.5)
     diffy (3.0.7)
     docile (1.1.5)
@@ -193,12 +198,12 @@ GEM
     email_spec (1.6.0)
       launchy (~> 2.1)
       mail (~> 2.2)
-    encryptor (1.3.0)
+    encryptor (3.0.0)
     equalizer (0.0.11)
     erubis (2.7.0)
     escape_utils (1.1.1)
     eventmachine (1.0.8)
-    excon (0.45.4)
+    excon (0.49.0)
     execjs (2.6.0)
     expression_parser (0.9.0)
     factory_girl (4.5.0)
@@ -215,8 +220,6 @@ GEM
       multi_json
     ffaker (2.0.0)
     ffi (1.9.10)
-    fission (0.5.0)
-      CFPropertyList (~> 2.2)
     flay (2.6.1)
       ruby_parser (~> 3.0)
       sexp_processor (~> 4.0)
@@ -226,109 +229,33 @@ GEM
     flowdock (0.7.1)
       httparty (~> 0.7)
       multi_json
-    fog (1.36.0)
-      fog-aliyun (>= 0.1.0)
-      fog-atmos
-      fog-aws (>= 0.6.0)
-      fog-brightbox (~> 0.4)
-      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)
-    fog-aliyun (0.1.0)
+    fog-aws (0.9.2)
       fog-core (~> 1.27)
       fog-json (~> 1.0)
+      fog-xml (~> 0.1)
       ipaddress (~> 0.8)
-      xml-simple (~> 1.1)
-    fog-atmos (0.1.0)
-      fog-core
-      fog-xml
-    fog-aws (0.8.1)
+    fog-azure (0.0.2)
+      azure (~> 0.6)
       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
-      inflecto (~> 0.0.2)
-    fog-core (1.35.0)
+    fog-core (1.40.0)
       builder
-      excon (~> 0.45)
+      excon (~> 0.49)
       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-google (0.3.2)
       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-local (0.3.0)
       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.5)
-      fog-core (>= 1.21.0)
-      fog-json
-      fog-xml (>= 0.0.1)
-    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-storm_on_demand (0.1.1)
-      fog-core
-      fog-json
-    fog-terremark (0.1.0)
-      fog-core
-      fog-xml
-    fog-vmfusion (0.1.0)
-      fission
-      fog-core
-    fog-voxel (0.1.0)
-      fog-core
-      fog-xml
-    fog-xenserver (0.2.2)
-      fog-core
-      fog-xml
+    fog-openstack (0.1.6)
+      fog-core (>= 1.39)
+      fog-json (>= 1.0)
+      ipaddress (>= 0.8)
     fog-xml (0.1.2)
       fog-core
       nokogiri (~> 1.5, >= 1.5.11)
@@ -372,7 +299,7 @@ GEM
     gitlab-license (0.0.4)
     gitlab_emoji (0.3.1)
       gemojione (~> 2.2, >= 2.2.1)
-    gitlab_git (10.1.0)
+    gitlab_git (10.1.3)
       activesupport (~> 4.0)
       charlock_holmes (~> 0.7.3)
       github-linguist (~> 4.7.0)
@@ -428,7 +355,6 @@ GEM
     hashie (3.4.3)
     health_check (1.5.1)
       rails (>= 2.3.0)
-    highline (1.7.8)
     hipchat (1.5.2)
       httparty
       mimemagic
@@ -448,11 +374,10 @@ GEM
     httpclient (2.7.0.1)
     i18n (0.7.0)
     ice_nine (0.11.1)
-    inflecto (0.0.2)
     influxdb (0.2.3)
       cause
       json
-    ipaddress (0.8.2)
+    ipaddress (0.8.3)
     jquery-atwho-rails (1.3.2)
     jquery-rails (4.1.1)
       rails-dom-testing (>= 1, < 3)
@@ -465,10 +390,13 @@ GEM
       railties (>= 3.2.16)
     json (1.8.3)
     jwt (1.5.2)
-    kaminari (0.16.3)
+    kaminari (0.17.0)
       actionpack (>= 3.0.0)
       activesupport (>= 3.0.0)
     kgio (2.10.0)
+    knapsack (1.11.0)
+      rake
+      timecop (>= 0.1.0)
     launchy (2.4.3)
       addressable (~> 2.3)
     letter_opener (1.4.1)
@@ -477,6 +405,12 @@ GEM
       actionmailer (>= 3.2)
       letter_opener (~> 1.0)
       railties (>= 3.2)
+    license_finder (2.1.0)
+      bundler
+      httparty
+      rubyzip
+      thor
+      xml-simple
     licensee (8.0.0)
       rugged (>= 0.24b)
     listen (3.0.5)
@@ -490,9 +424,9 @@ GEM
       mime-types (>= 1.16, < 4)
     mail_room (0.7.0)
     method_source (0.8.2)
-    mime-types (2.99.1)
+    mime-types (2.99.2)
     mimemagic (0.3.0)
-    mini_portile2 (2.0.0)
+    mini_portile2 (2.1.0)
     minitest (5.7.0)
     mousetrap-rails (1.4.6)
     multi_json (1.11.2)
@@ -503,8 +437,10 @@ GEM
     net-ldap (0.12.1)
     net-ssh (3.0.1)
     newrelic_rpm (3.14.1.311)
-    nokogiri (1.6.7.2)
-      mini_portile2 (~> 2.0.0.rc2)
+    nokogiri (1.6.8)
+      mini_portile2 (~> 2.1.0)
+      pkg-config (~> 1.1.7)
+    numerizer (0.1.1)
     oauth (0.4.7)
     oauth2 (1.0.0)
       faraday (>= 0.8, < 0.10)
@@ -573,9 +509,10 @@ GEM
     orm_adapter (0.5.0)
     paranoia (2.1.4)
       activerecord (~> 4.0)
-    parser (2.3.0.6)
+    parser (2.3.1.0)
       ast (~> 2.2)
     pg (0.18.4)
+    pkg-config (1.1.7)
     poltergeist (1.9.0)
       capybara (~> 2.1)
       cliver (~> 0.3.1)
@@ -643,7 +580,6 @@ GEM
     rainbow (2.1.0)
     raindrops (0.15.0)
     rake (10.5.0)
-    raphael-rails (2.1.2)
     rb-fsevent (0.9.6)
     rb-inotify (0.9.5)
       ffi (>= 0.5.0)
@@ -651,7 +587,7 @@ GEM
       debugger-ruby_core_source (~> 1.3)
     rdoc (3.12.2)
       json (~> 1.4)
-    recaptcha (1.0.2)
+    recaptcha (3.0.0)
       json
     redcarpet (3.3.3)
     redis (3.3.0)
@@ -679,8 +615,8 @@ GEM
     responders (2.1.1)
       railties (>= 4.2.0, < 5.1)
     rinku (1.7.3)
-    rotp (2.1.1)
-    rouge (1.10.1)
+    rotp (2.1.2)
+    rouge (1.11.0)
     rqrcode (0.7.0)
       chunky_png
     rqrcode-rails3 (0.1.7)
@@ -708,31 +644,31 @@ GEM
     rspec-retry (0.4.5)
       rspec-core
     rspec-support (3.4.1)
-    rubocop (0.38.0)
-      parser (>= 2.3.0.6, < 3.0)
+    rubocop (0.40.0)
+      parser (>= 2.3.1.0, < 3.0)
       powerpack (~> 0.1)
       rainbow (>= 1.99.1, < 3.0)
       ruby-progressbar (~> 1.7)
       unicode-display_width (~> 1.0, >= 1.0.1)
+    rubocop-rspec (1.5.0)
+      rubocop (>= 0.40.0)
     ruby-fogbugz (0.2.1)
       crack (~> 0.4)
-    ruby-progressbar (1.7.5)
+    ruby-progressbar (1.8.1)
     ruby-saml (1.1.2)
       nokogiri (>= 1.5.10)
       uuid (~> 2.3)
-    ruby2ruby (2.3.0)
-      ruby_parser (~> 3.1)
-      sexp_processor (~> 4.0)
-    ruby_parser (3.8.1)
+    ruby_parser (3.8.2)
       sexp_processor (~> 4.1)
     rubyntlm (0.5.2)
     rubypants (0.2.0)
+    rubyzip (1.2.0)
     rufus-scheduler (3.1.10)
     rugged (0.24.0)
     safe_yaml (1.0.4)
     sanitize (2.1.0)
       nokogiri (>= 1.4.4)
-    sass (3.4.21)
+    sass (3.4.22)
     sass-rails (5.0.4)
       railties (>= 4.0.0, < 5.0)
       sass (~> 3.1)
@@ -781,9 +717,6 @@ GEM
       tilt (>= 1.3, < 3)
     six (0.2.0)
     slack-notifier (1.2.1)
-    slim (3.0.6)
-      temple (~> 0.7.3)
-      tilt (>= 1.3.3, < 2.1)
     slop (3.6.0)
     spinach (0.8.10)
       colorize
@@ -810,11 +743,11 @@ GEM
       activesupport (>= 4.0)
       sprockets (>= 3.0.0)
     state_machines (0.4.0)
-    state_machines-activemodel (0.3.0)
-      activemodel (~> 4.1)
+    state_machines-activemodel (0.4.0)
+      activemodel (>= 4.1, < 5.1)
       state_machines (>= 0.4.0)
-    state_machines-activerecord (0.3.0)
-      activerecord (~> 4.1)
+    state_machines-activerecord (0.4.0)
+      activerecord (>= 4.1, < 5.1)
       state_machines-activemodel (>= 0.3.0)
     stringex (2.5.2)
     systemu (2.6.5)
@@ -824,10 +757,8 @@ GEM
       railties (>= 3.2.5, < 6)
     teaspoon-jasmine (2.2.0)
       teaspoon (>= 1.0.0)
-    temple (0.7.6)
     term-ansicolor (1.3.2)
       tins (~> 1.0)
-    terminal-table (1.5.2)
     test_after_commit (0.4.2)
       activerecord (>= 3.2)
     thin (1.6.4)
@@ -836,7 +767,8 @@ GEM
       rack (~> 1.0)
     thor (0.19.1)
     thread_safe (0.3.5)
-    tilt (2.0.2)
+    tilt (2.0.5)
+    timecop (0.8.1)
     timfel-krb5-auth (0.8.3)
     tinder (1.10.1)
       eventmachine (~> 1.0)
@@ -856,6 +788,7 @@ GEM
       simple_oauth (~> 0.1.4)
     tzinfo (1.2.2)
       thread_safe (~> 0.1)
+    u2f (0.2.1)
     uglifier (2.7.2)
       execjs (>= 0.3.0)
       json (>= 1.8.0)
@@ -863,7 +796,7 @@ GEM
     unf (0.1.4)
       unf_ext
     unf_ext (0.0.7.2)
-    unicode-display_width (1.0.2)
+    unicode-display_width (1.0.5)
     unicorn (4.9.0)
       kgio (~> 2.6)
       rack
@@ -883,7 +816,7 @@ GEM
       coercible (~> 1.0)
       descendants_tracker (~> 0.0, >= 0.0.3)
       equalizer (~> 0.0, >= 0.0.9)
-    warden (1.2.4)
+    warden (1.2.6)
       rack (>= 1.0)
     web-console (2.3.0)
       activemodel (>= 4.0)
@@ -910,7 +843,7 @@ PLATFORMS
 DEPENDENCIES
   RedCloth (~> 4.2.9)
   ace-rails-ap (~> 4.0.2)
-  activerecord-session_store (~> 0.1.0)
+  activerecord-session_store (~> 1.0.0)
   acts-as-taggable-on (~> 3.4)
   addressable (~> 2.3.8)
   after_commit_queue
@@ -918,7 +851,7 @@ DEPENDENCIES
   allocations (~> 1.0)
   asana (~> 0.4.0)
   asciidoctor (~> 1.5.2)
-  attr_encrypted (~> 1.3.4)
+  attr_encrypted (~> 3.0.0)
   awesome_print (~> 1.2.0)
   babosa (~> 1.0.2)
   base32 (~> 0.3.0)
@@ -926,27 +859,25 @@ DEPENDENCIES
   better_errors (~> 1.0.1)
   binding_of_caller (~> 0.7.2)
   bootstrap-sass (~> 3.3.0)
-  brakeman (~> 3.2.0)
-  browser (~> 1.0.0)
+  brakeman (~> 3.3.0)
+  browser (~> 2.0.3)
   bullet
   bundler-audit
   byebug
-  cal-heatmap-rails (~> 3.6.0)
   capybara (~> 2.6.2)
   capybara-screenshot (~> 1.0.0)
   carrierwave (~> 0.10.0)
   charlock_holmes (~> 0.7.3)
+  chronic_duration (~> 0.10.6)
   coffee-rails (~> 4.1.0)
-  colorize (~> 0.7.0)
   connection_pool (~> 2.0)
   coveralls (~> 0.8.2)
   creole (~> 0.5.0)
   d3_rails (~> 3.5.0)
   database_cleaner (~> 1.4.0)
   default_value_for (~> 3.0.0)
-  devise (~> 3.5.4)
-  devise-async (~> 0.9.0)
-  devise-two-factor (~> 2.0.0)
+  devise (~> 4.0)
+  devise-two-factor (~> 3.0.0)
   diffy (~> 3.0.3)
   doorkeeper (~> 3.1)
   dropzonejs-rails (~> 0.7.1)
@@ -958,7 +889,12 @@ DEPENDENCIES
   ffaker (~> 2.0.0)
   flay
   flog
-  fog (~> 1.36.0)
+  fog-aws (~> 0.9)
+  fog-azure (~> 0.0)
+  fog-core (~> 1.40)
+  fog-google (~> 0.3)
+  fog-local (~> 0.3)
+  fog-openstack (~> 0.1)
   font-awesome-rails (~> 4.2)
   foreman
   fuubar (~> 2.0.0)
@@ -989,8 +925,10 @@ DEPENDENCIES
   jquery-turbolinks (~> 2.1.0)
   jquery-ui-rails (~> 5.0.0)
   jwt
-  kaminari (~> 0.16.3)
+  kaminari (~> 0.17.0)
+  knapsack
   letter_opener_web (~> 1.3.0)
+  license_finder
   licensee (~> 8.0.0)
   loofah (~> 2.0.3)
   mail_room (~> 0.7)
@@ -1031,10 +969,10 @@ DEPENDENCIES
   rack-oauth2 (~> 1.2.1)
   rails (= 4.2.6)
   rails-deprecated_sanitizer (~> 1.0.3)
-  raphael-rails (~> 2.1.2)
+  rainbow (~> 2.1.0)
   rblineprof
   rdoc (~> 3.6)
-  recaptcha
+  recaptcha (~> 3.0)
   redcarpet (~> 3.3.3)
   redis (~> 3.2)
   redis-namespace
@@ -1042,11 +980,12 @@ DEPENDENCIES
   request_store (~> 1.3.0)
   rerun (~> 0.11.0)
   responders (~> 2.0)
-  rouge (~> 1.10.1)
+  rouge (~> 1.11)
   rqrcode-rails3 (~> 0.1.7)
   rspec-rails (~> 3.4.0)
   rspec-retry
-  rubocop (~> 0.38.0)
+  rubocop (~> 0.40.0)
+  rubocop-rspec (~> 1.5.0)
   ruby-fogbugz (~> 0.2.1)
   sanitize (~> 2.0)
   sass-rails (~> 5.0.0)
@@ -1071,7 +1010,7 @@ DEPENDENCIES
   spring-commands-spinach (~> 1.1.0)
   spring-commands-teaspoon (~> 0.0.2)
   sprockets (~> 3.6.0)
-  state_machines-activerecord (~> 0.3.0)
+  state_machines-activerecord (~> 0.4.0)
   task_list (~> 1.0.2)
   teaspoon (~> 1.1.0)
   teaspoon-jasmine (~> 2.2.0)
@@ -1079,6 +1018,7 @@ DEPENDENCIES
   thin (~> 1.6.1)
   tinder (~> 1.10.0)
   turbolinks (~> 2.5.0)
+  u2f (~> 0.2.1)
   uglifier (~> 2.7.2)
   underscore-rails (~> 1.8.0)
   unf (~> 0.1.4)
@@ -1092,4 +1032,4 @@ DEPENDENCIES
   wikicloth (= 0.8.1)
 
 BUNDLED WITH
-   1.12.3
+   1.12.5
diff --git a/README.md b/README.md
index 755b76a6ea67c28dcfa421df7eb60869009adfec..5885c4d18313feaa2da7e3dfcf4f64885c33dd26 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,7 @@
 # GitLab
 
 [![build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
-[![Build Status](https://semaphoreci.com/api/v1/projects/2f1a5809-418b-4cc2-a1f4-819607579fe7/400484/shields_badge.svg)](https://semaphoreci.com/gitlabhq/gitlabhq)
 [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
-[![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.svg?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq?branch=master)
 
 ## Canonical source
 
diff --git a/Rakefile b/Rakefile
index 5dd389d5678e1e7aeb90b4c879d1dbe150b96b40..85fff2d51eb015ead78cd9428747ce6768eeebe1 100755
--- a/Rakefile
+++ b/Rakefile
@@ -8,3 +8,5 @@ relative_url_conf = File.expand_path('../config/initializers/relative_url', __FI
 require relative_url_conf if File.exist?("#{relative_url_conf}.rb")
 
 Gitlab::Application.load_tasks
+
+Knapsack.load_tasks if defined?(Knapsack)
diff --git a/app/assets/images/mailers/gitlab_header_logo.png b/app/assets/images/mailers/gitlab_header_logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..35ca1860887f36346d7646ad9b51c79d8c811b60
Binary files /dev/null and b/app/assets/images/mailers/gitlab_header_logo.png differ
diff --git a/app/assets/images/mailers/gitlab_tanuki_2x.png b/app/assets/images/mailers/gitlab_tanuki_2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..551dd6ce2ce88915a21ebad72f8d5cafc4e6fd3c
Binary files /dev/null and b/app/assets/images/mailers/gitlab_tanuki_2x.png differ
diff --git a/app/assets/javascripts/LabelManager.js.coffee b/app/assets/javascripts/LabelManager.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..b06bcf0fcbf33c007ff8f0135a3e2404a3abb61c
--- /dev/null
+++ b/app/assets/javascripts/LabelManager.js.coffee
@@ -0,0 +1,87 @@
+class @LabelManager
+  errorMessage: 'Unable to update label prioritization at this time'
+
+  constructor: (opts = {}) ->
+    # Defaults
+    {
+      @togglePriorityButton = $('.js-toggle-priority')
+      @prioritizedLabels = $('.js-prioritized-labels')
+      @otherLabels = $('.js-other-labels')
+    } = opts
+
+    @prioritizedLabels.sortable(
+      items: 'li'
+      placeholder: 'list-placeholder'
+      axis: 'y'
+      update: @onPrioritySortUpdate.bind(@)
+    )
+
+    @bindEvents()
+
+  bindEvents: ->
+    @togglePriorityButton.on 'click', @, @onTogglePriorityClick
+
+  onTogglePriorityClick: (e) ->
+    e.preventDefault()
+    _this = e.data
+    $btn = $(e.currentTarget)
+    $label = $("##{$btn.data('domId')}")
+    action = if $btn.parents('.js-prioritized-labels').length then 'remove' else 'add'
+    _this.toggleLabelPriority($label, action)
+
+  toggleLabelPriority: ($label, action, persistState = true) ->
+    _this = @
+    url = $label.find('.js-toggle-priority').data 'url'
+
+    $target = @prioritizedLabels
+    $from = @otherLabels
+
+    # Optimistic update
+    if action is 'remove'
+      $target = @otherLabels
+      $from = @prioritizedLabels
+
+    if $from.find('li').length is 1
+      $from.find('.empty-message').removeClass('hidden')
+
+    if not $target.find('li').length
+      $target.find('.empty-message').addClass('hidden')
+
+    $label.detach().appendTo($target)
+
+    # Return if we are not persisting state
+    return unless persistState
+
+    if action is 'remove'
+      xhr = $.ajax url: url, type: 'DELETE'
+
+      # Restore empty message
+      $from.find('.empty-message').removeClass('hidden') unless $from.find('li').length
+    else
+      xhr = @savePrioritySort($label, action)
+
+    xhr.fail @rollbackLabelPosition.bind(@, $label, action)
+
+  onPrioritySortUpdate: ->
+    xhr = @savePrioritySort()
+
+    xhr.fail ->
+      new Flash(@errorMessage, 'alert')
+
+  savePrioritySort: () ->
+    $.post
+      url: @prioritizedLabels.data('url')
+      data:
+        label_ids: @getSortedLabelsIds()
+
+  rollbackLabelPosition: ($label, originalAction)->
+    action = if originalAction is 'remove' then 'add' else 'remove'
+    @toggleLabelPriority($label, action, false)
+
+    new Flash(@errorMessage, 'alert')
+
+  getSortedLabelsIds: ->
+    sortedIds = []
+    @prioritizedLabels.find('li').each ->
+      sortedIds.push $(@).data 'id'
+    sortedIds
diff --git a/app/assets/javascripts/activities.js.coffee b/app/assets/javascripts/activities.js.coffee
index 5092e824e654e7d262e5af39e7e390197380774b..ed5a5d0260ce70e06791f20424639fed0864aa49 100644
--- a/app/assets/javascripts/activities.js.coffee
+++ b/app/assets/javascripts/activities.js.coffee
@@ -1,11 +1,14 @@
 class @Activities
   constructor: ->
-    Pager.init 20, true
+    Pager.init 20, true, false, @updateTooltips
     $(".event-filter-link").on "click", (event) =>
       event.preventDefault()
       @toggleFilter($(event.currentTarget))
       @reloadActivities()
 
+  updateTooltips: ->
+    gl.utils.localTimeAgo($('.js-timeago', '#activity'))
+
   reloadActivities: ->
     $(".content_list").html ''
     Pager.init 20, true
diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee
index 8ce5bf9825797a4578ff94786d3110dd397c0961..84a87fadd0bf66ca11b11dcb9746f958ef5430d1 100644
--- a/app/assets/javascripts/api.js.coffee
+++ b/app/assets/javascripts/api.js.coffee
@@ -1,16 +1,16 @@
 @Api =
-  groups_path: "/api/:version/groups.json"
-  group_path: "/api/:version/groups/:id.json"
-  projects_path: "/api/:version/projects.json"
-  ldap_groups_path: "/api/:version/ldap/:provider/groups.json"
-  namespaces_path: "/api/:version/namespaces.json"
-  group_projects_path: "/api/:version/groups/:id/projects.json"
-  projects_path: "/api/:version/projects.json"
-  labels_path: "/api/:version/projects/:id/labels"
-  license_path: "/api/:version/licenses/:key"
+  groupsPath: "/api/:version/groups.json"
+  groupPath: "/api/:version/groups/:id.json"
+  namespacesPath: "/api/:version/namespaces.json"
+  groupProjectsPath: "/api/:version/groups/:id/projects.json"
+  projectsPath: "/api/:version/projects.json"
+  labelsPath: "/api/:version/projects/:id/labels"
+  licensePath: "/api/:version/licenses/:key"
+  gitignorePath: "/api/:version/gitignores/:key"
+  ldapGroupsPath: "/api/:version/ldap/:provider/groups.json"
 
   group: (group_id, callback) ->
-    url = Api.buildUrl(Api.group_path)
+    url = Api.buildUrl(Api.groupPath)
     url = url.replace(':id', group_id)
 
     $.ajax(
@@ -24,7 +24,7 @@
   # Return groups list. Filtered by query
   # Only active groups retrieved
   groups: (query, skip_ldap, callback) ->
-    url = Api.buildUrl(Api.groups_path)
+    url = Api.buildUrl(Api.groupsPath)
 
     $.ajax(
       url: url
@@ -38,7 +38,7 @@
 
   # Return namespaces list. Filtered by query
   namespaces: (query, callback) ->
-    url = Api.buildUrl(Api.namespaces_path)
+    url = Api.buildUrl(Api.namespacesPath)
 
     $.ajax(
       url: url
@@ -52,7 +52,7 @@
 
   # Return projects list. Filtered by query
   projects: (query, order, callback) ->
-    url = Api.buildUrl(Api.projects_path)
+    url = Api.buildUrl(Api.projectsPath)
 
     $.ajax(
       url: url
@@ -66,7 +66,7 @@
       callback(projects)
 
   newLabel: (project_id, data, callback) ->
-    url = Api.buildUrl(Api.labels_path)
+    url = Api.buildUrl(Api.labelsPath)
     url = url.replace(':id', project_id)
 
     data.private_token = gon.api_token
@@ -82,7 +82,7 @@
 
   # Return group projects list. Filtered by query
   groupProjects: (group_id, query, callback) ->
-    url = Api.buildUrl(Api.group_projects_path)
+    url = Api.buildUrl(Api.groupProjectsPath)
     url = url.replace(':id', group_id)
 
     $.ajax(
@@ -97,7 +97,7 @@
 
   # Return text for a specific license
   licenseText: (key, data, callback) ->
-    url = Api.buildUrl(Api.license_path).replace(':key', key)
+    url = Api.buildUrl(Api.licensePath).replace(':key', key)
 
     $.ajax(
       url: url
@@ -105,13 +105,19 @@
     ).done (license) ->
       callback(license)
 
+  gitignoreText: (key, callback) ->
+    url = Api.buildUrl(Api.gitignorePath).replace(':key', key)
+
+    $.get url, (gitignore) ->
+      callback(gitignore)
+
   buildUrl: (url) ->
     url = gon.relative_url_root + url if gon.relative_url_root?
     return url.replace(':version', gon.api_version)
 
   # Return LDAP groups list. Filtered by query
   ldap_groups: (query, provider, callback) ->
-    url = Api.buildUrl(Api.ldap_groups_path)
+    url = Api.buildUrl(Api.ldapGroupsPath)
     url = url.replace(':provider', provider);
 
     $.ajax(
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index e9db8b8dee13050d14a162f8197779d2d0032f56..58fd30dd919ad7d0052cf04eb0186e16764013b3 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -4,7 +4,7 @@
 # It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
 # the compiled file.
 #
-#= require jquery
+#= require jquery2
 #= require jquery-ui/autocomplete
 #= require jquery-ui/datepicker
 #= require jquery-ui/draggable
@@ -19,8 +19,6 @@
 #= require jquery.scrollTo
 #= require jquery.turbolinks
 #= require jquery.tablesorter
-#= require d3
-#= require cal-heatmap
 #= require turbolinks
 #= require autosave
 #= require bootstrap/affix
@@ -35,11 +33,6 @@
 #= require bootstrap/tooltip
 #= require bootstrap/popover
 #= require select2
-#= require raphael
-#= require g.raphael
-#= require g.bar
-#= require Chart
-#= require branch-graph
 #= require ace/ace
 #= require ace/ext-searchbox
 #= require underscore
@@ -53,9 +46,17 @@
 #= require shortcuts_network
 #= require jquery.nicescroll
 #= require date.format
-#= require_tree .
+#= require_directory ./behaviors
+#= require_directory ./blob
+#= require_directory ./ci
+#= require_directory ./commit
+#= require_directory ./extensions
+#= require_directory ./lib
+#= require_directory ./u2f
+#= require_directory .
 #= require fuzzaldrin-plus
 #= require cropper
+#= require u2f
 
 window.slugify = (text) ->
   text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
@@ -123,9 +124,10 @@ window.onload = ->
     setTimeout shiftWindow, 100
 
 $ ->
+  gl.utils.preventDisabledButtons()
   bootstrapBreakpoint = bp.getBreakpointSize()
 
-  $(".nicescroll").niceScroll(cursoropacitymax: '0.4', cursorcolor: '#FFF', cursorborder: "1px solid #FFF")
+  $(".nav-sidebar").niceScroll(cursoropacitymax: '0.4', cursorcolor: '#FFF', cursorborder: "1px solid #FFF")
 
   # Click a .js-select-on-focus field, select the contents
   $(".js-select-on-focus").on "focusin", ->
@@ -160,19 +162,6 @@ $ ->
       $el.data('placement') || 'bottom'
   )
 
-  $('.header-logo .home').tooltip(
-    placement: (_, el) ->
-      $el = $(el)
-      if $('.page-with-sidebar').hasClass('page-sidebar-collapsed') then 'right' else 'bottom'
-    container: 'body'
-  )
-
-  $('.page-with-sidebar').tooltip(
-    selector: '.sidebar-collapsed .nav-sidebar a, .sidebar-collapsed a.sidebar-user'
-    placement: 'right'
-    container: 'body'
-  )
-
   # Form submitter
   $('.trigger-submit').on 'change', ->
     $(@).parents('form').submit()
@@ -205,6 +194,7 @@ $ ->
 
   $('.navbar-toggle').on 'click', ->
     $('.header-content .title').toggle()
+    $('.header-content .header-logo').toggle()
     $('.header-content .navbar-collapse').toggle()
     $('.navbar-toggle').toggleClass('active')
     $('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left")
@@ -224,6 +214,10 @@ $ ->
     form = btn.closest("form")
     new ConfirmDangerModal(form, text, warningMessage: warningMessage)
 
+
+  $(document).on 'click', 'button', ->
+    $(this).blur()
+
   $('input[type="search"]').each ->
     $this = $(this)
     $this.attr 'value', $this.val()
@@ -236,7 +230,6 @@ $ ->
       $this.attr 'value', $this.val()
 
   $sidebarGutterToggle = $('.js-sidebar-toggle')
-  $navIconToggle = $('.toggle-nav-collapse')
 
   $(document)
     .off 'breakpoint:change'
@@ -246,42 +239,6 @@ $ ->
         if $gutterIcon.hasClass('fa-angle-double-right')
           $sidebarGutterToggle.trigger('click')
 
-        $navIcon = $navIconToggle.find('.fa')
-        if $navIcon.hasClass('fa-angle-left')
-          $navIconToggle.trigger('click')
-
-  $(document)
-    .off 'click', '.js-sidebar-toggle'
-    .on 'click', '.js-sidebar-toggle', (e, triggered) ->
-      e.preventDefault()
-      $this = $(this)
-      $thisIcon = $this.find 'i'
-      $allGutterToggleIcons = $('.js-sidebar-toggle i')
-      if $thisIcon.hasClass('fa-angle-double-right')
-        $allGutterToggleIcons
-          .removeClass('fa-angle-double-right')
-          .addClass('fa-angle-double-left')
-        $('aside.right-sidebar')
-          .removeClass('right-sidebar-expanded')
-          .addClass('right-sidebar-collapsed')
-        $('.page-with-sidebar')
-          .removeClass('right-sidebar-expanded')
-          .addClass('right-sidebar-collapsed')
-      else
-        $allGutterToggleIcons
-          .removeClass('fa-angle-double-left')
-          .addClass('fa-angle-double-right')
-        $('aside.right-sidebar')
-          .removeClass('right-sidebar-collapsed')
-          .addClass('right-sidebar-expanded')
-        $('.page-with-sidebar')
-          .removeClass('right-sidebar-collapsed')
-          .addClass('right-sidebar-expanded')
-      if not triggered
-        $.cookie("collapsed_gutter",
-          $('.right-sidebar')
-            .hasClass('right-sidebar-collapsed'), { path: '/' })
-
   fitSidebarForSize = ->
     oldBootstrapBreakpoint = bootstrapBreakpoint
     bootstrapBreakpoint = bp.getBreakpointSize()
@@ -294,9 +251,38 @@ $ ->
       $(document).trigger('breakpoint:change', [bootstrapBreakpoint])
 
   $(window)
-    .off "resize"
-    .on "resize", (e) ->
+    .off "resize.app"
+    .on "resize.app", (e) ->
       fitSidebarForSize()
 
+  gl.awardsHandler = new AwardsHandler()
   checkInitialSidebarSize()
   new Aside()
+
+  # Sidenav pinning
+  if $(window).width() < 1440 and $.cookie('pin_nav') is 'true'
+    $.cookie('pin_nav', 'false')
+    $('.page-with-sidebar')
+      .toggleClass('page-sidebar-collapsed page-sidebar-expanded')
+      .removeClass('page-sidebar-pinned')
+    $('.navbar-fixed-top').removeClass('header-pinned-nav')
+
+  $(document)
+    .off 'click', '.js-nav-pin'
+    .on 'click', '.js-nav-pin', (e) ->
+      e.preventDefault()
+
+      $(this).toggleClass 'is-active'
+
+      if $.cookie('pin_nav') is 'true'
+        $.cookie 'pin_nav', 'false'
+        $('.page-with-sidebar')
+          .removeClass('page-sidebar-pinned')
+          .toggleClass('page-sidebar-collapsed page-sidebar-expanded')
+        $('.navbar-fixed-top')
+          .removeClass('header-pinned-nav')
+          .toggleClass('header-collapsed header-expanded')
+      else
+        $.cookie 'pin_nav', 'true'
+        $('.page-with-sidebar').addClass('page-sidebar-pinned')
+        $('.navbar-fixed-top').addClass('header-pinned-nav')
diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee
index bf95e06b4e5e8336168402982c0a3bbc3b4aefbe..030f1564862631aeaf4140f15756e58abac8c0a1 100644
--- a/app/assets/javascripts/awards_handler.coffee
+++ b/app/assets/javascripts/awards_handler.coffee
@@ -1,201 +1,354 @@
 class @AwardsHandler
-  constructor: (@getEmojisUrl, @postEmojiUrl, @noteableType, @noteableId, @unicodes) ->
-    $('.js-add-award').on 'click', (event) =>
-      event.stopPropagation()
-      event.preventDefault()
 
-      @showEmojiMenu()
+  constructor: ->
 
-    $('html').on 'click', (event) ->
-      if !$(event.target).closest('.emoji-menu').length
+    @aliases = gl.emojiAliases()
+
+    $(document)
+      .off 'click', '.js-add-award'
+      .on  'click', '.js-add-award', (e) =>
+        e.stopPropagation()
+        e.preventDefault()
+
+        @showEmojiMenu $(e.currentTarget)
+
+    $('html').on 'click', (e) ->
+      $target = $ e.target
+
+      unless $target.closest('.emoji-menu-content').length
+        $('.js-awards-block.current').removeClass 'current'
+
+      unless $target.closest('.emoji-menu').length
         if $('.emoji-menu').is(':visible')
+          $('.js-add-award.is-active').removeClass 'is-active'
           $('.emoji-menu').removeClass 'is-visible'
 
-    $('.awards')
-      .off 'click'
-      .on 'click', '.js-emoji-btn', @handleClick
+    $(document)
+      .off 'click', '.js-emoji-btn'
+      .on  'click', '.js-emoji-btn', (e) =>
+        e.preventDefault()
 
-    @renderFrequentlyUsedBlock()
+        $target = $ e.currentTarget
+        emoji   = $target.find('.icon').data 'emoji'
 
-  handleClick: (e) ->
-    e.preventDefault()
-    emoji = $(this)
-      .find('.icon')
-      .data 'emoji'
+        $target.closest('.js-awards-block').addClass 'current'
+        @addAward @getVotesBlock(), @getAwardUrl(), emoji
 
-    if emoji is 'thumbsup' and awardsHandler.didUserClickEmoji $(this), 'thumbsdown'
-      awardsHandler.addAward 'thumbsdown'
 
-    else if emoji is 'thumbsdown' and awardsHandler.didUserClickEmoji $(this), 'thumbsup'
-      awardsHandler.addAward 'thumbsup'
+  showEmojiMenu: ($addBtn) ->
 
-    awardsHandler.addAward emoji
+    $menu = $ '.emoji-menu'
 
-    $(this).trigger 'blur'
+    if $addBtn.hasClass 'js-note-emoji'
+      $addBtn.closest('.note').find('.js-awards-block').addClass 'current'
+    else
+      $addBtn.closest('.js-awards-block').addClass 'current'
 
-  didUserClickEmoji: (that, emoji) ->
-    if $(that).siblings("button:has([data-emoji=#{emoji}])").attr('data-original-title')
-      $(that).siblings("button:has([data-emoji=#{emoji}])").attr('data-original-title').indexOf('me') > -1
+    if $menu.length
+      $holder = $addBtn.closest('.js-award-holder')
 
-  showEmojiMenu: ->
-    if $('.emoji-menu').length
-      if $('.emoji-menu').is '.is-visible'
-        $('.emoji-menu').removeClass 'is-visible'
+      if $menu.is '.is-visible'
+        $addBtn.removeClass 'is-active'
+        $menu.removeClass 'is-visible'
         $('#emoji_search').blur()
       else
-        $('.emoji-menu').addClass 'is-visible'
+        $addBtn.addClass 'is-active'
+        @positionMenu($menu, $addBtn)
+
+        $menu.addClass 'is-visible'
         $('#emoji_search').focus()
     else
-      $('.js-add-award').addClass 'is-loading'
-      $.get @getEmojisUrl, (response) =>
-        $('.js-add-award').removeClass 'is-loading'
-        $('.js-award-holder').append response
+      $addBtn.addClass 'is-loading is-active'
+      url = @getAwardMenuUrl()
+
+      @createEmojiMenu url, =>
+        $addBtn.removeClass 'is-loading'
+        $menu = $('.emoji-menu')
+        @positionMenu($menu, $addBtn)
+        @renderFrequentlyUsedBlock() unless @frequentEmojiBlockRendered
+
         setTimeout =>
-          $('.emoji-menu').addClass 'is-visible'
+          $menu.addClass 'is-visible'
           $('#emoji_search').focus()
           @setupSearch()
         , 200
 
-  addAward: (emoji) ->
-    @postEmoji emoji, =>
-      @addAwardToEmojiBar(emoji)
+
+  createEmojiMenu: (awardMenuUrl, callback) ->
+
+    $.get awardMenuUrl, (response) ->
+      $('body').append response
+      callback()
+
+
+  positionMenu: ($menu, $addBtn) ->
+
+    position = $addBtn.data('position')
+
+    # The menu could potentially be off-screen or in a hidden overflow element
+    # So we position the element absolute in the body
+    css =
+      top: "#{$addBtn.offset().top + $addBtn.outerHeight()}px"
+
+    if position? and position is 'right'
+      css.left = "#{($addBtn.offset().left - $menu.outerWidth()) + 20}px"
+      $menu.addClass 'is-aligned-right'
+    else
+      css.left = "#{$addBtn.offset().left}px"
+      $menu.removeClass 'is-aligned-right'
+
+    $menu.css(css)
+
+
+  addAward: (votesBlock, awardUrl, emoji, checkMutuality = true, callback) ->
+
+    emoji = @normilizeEmojiName emoji
+
+    @postEmoji awardUrl, emoji, =>
+      @addAwardToEmojiBar votesBlock, emoji, checkMutuality
+      callback?()
 
     $('.emoji-menu').removeClass 'is-visible'
 
-  addAwardToEmojiBar: (emoji) ->
-    @addEmojiToFrequentlyUsedList(emoji)
 
-    if @exist(emoji)
-      if @isActive(emoji)
-        @decrementCounter(emoji)
+  addAwardToEmojiBar: (votesBlock, emoji, checkForMutuality = true) ->
+
+    @checkMutuality votesBlock, emoji  if checkForMutuality
+    @addEmojiToFrequentlyUsedList emoji
+
+    emoji        = @normilizeEmojiName emoji
+    $emojiButton = @findEmojiIcon(votesBlock, emoji).parent()
+
+    if $emojiButton.length > 0
+      if @isActive $emojiButton
+        @decrementCounter $emojiButton, emoji
       else
-        counter = @findEmojiIcon(emoji).siblings('.js-counter')
-        counter.text(parseInt(counter.text()) + 1)
-        counter.parent().addClass('active')
-        @addMeToAuthorList(emoji)
+        counter = $emojiButton.find '.js-counter'
+        counter.text parseInt(counter.text()) + 1
+        $emojiButton.addClass 'active'
+        @addMeToUserList votesBlock, emoji
+        @animateEmoji $emojiButton
     else
-      @createEmoji(emoji)
-
-  exist: (emoji) ->
-    @findEmojiIcon(emoji).length > 0
-
-  isActive: (emoji) ->
-    @findEmojiIcon(emoji).parent().hasClass('active')
-
-  decrementCounter: (emoji) ->
-    counter = @findEmojiIcon(emoji).siblings('.js-counter')
-    emojiIcon = counter.parent()
-    if parseInt(counter.text()) > 1
-      counter.text(parseInt(counter.text()) - 1)
-      emojiIcon.removeClass('active')
-      @removeMeFromAuthorList(emoji)
-    else if emoji == 'thumbsup' || emoji == 'thumbsdown'
-      emojiIcon.tooltip('destroy')
-      counter.text(0)
-      emojiIcon.removeClass('active')
-      @removeMeFromAuthorList(emoji)
+      votesBlock.removeClass 'hidden'
+      @createEmoji votesBlock, emoji
+
+
+  getVotesBlock: ->
+
+    currentBlock = $ '.js-awards-block.current'
+    return if currentBlock.length then currentBlock else $('.js-awards-block').eq 0
+
+
+  getAwardUrl: -> return @getVotesBlock().data 'award-url'
+
+
+  checkMutuality: (votesBlock, emoji) ->
+
+    awardUrl = @getAwardUrl()
+
+    if emoji in [ 'thumbsup', 'thumbsdown' ]
+      mutualVote     = if emoji is 'thumbsup' then 'thumbsdown' else 'thumbsup'
+      $emojiButton   = votesBlock.find("[data-emoji=#{mutualVote}]").parent()
+      isAlreadyVoted = $emojiButton.hasClass 'active'
+
+      if isAlreadyVoted
+        @showEmojiLoader $emojiButton
+        @addAward votesBlock, awardUrl, mutualVote, false, ->
+          $emojiButton.removeClass 'is-loading'
+
+
+  showEmojiLoader: ($emojiButton) ->
+
+    $loader = $emojiButton.find '.fa-spinner'
+
+    unless $loader.length
+      $emojiButton.append '<i class="fa fa-spinner fa-spin award-control-icon award-control-icon-loading"></i>'
+
+    $emojiButton.addClass 'is-loading'
+
+
+  isActive: ($emojiButton) -> $emojiButton.hasClass 'active'
+
+
+  decrementCounter: ($emojiButton, emoji) ->
+
+    counter       = $ '.js-counter', $emojiButton
+    counterNumber = parseInt counter.text(), 10
+
+    if counterNumber > 1
+      counter.text counterNumber - 1
+      @removeMeFromUserList $emojiButton, emoji
+    else if emoji is 'thumbsup' or emoji is 'thumbsdown'
+      $emojiButton.tooltip 'destroy'
+      counter.text '0'
+      @removeMeFromUserList $emojiButton, emoji
+      @removeEmoji $emojiButton if $emojiButton.parents('.note').length
     else
-      emojiIcon.tooltip('destroy')
-      emojiIcon.remove()
-
-  removeMeFromAuthorList: (emoji) ->
-    awardBlock = @findEmojiIcon(emoji).parent()
-    authors = awardBlock
-      .attr('data-original-title')
-      .split(', ')
-    authors.splice(authors.indexOf('me'),1)
+      @removeEmoji $emojiButton
+
+    $emojiButton.removeClass 'active'
+
+
+  removeEmoji: ($emojiButton) ->
+
+    $emojiButton.tooltip('destroy')
+    $emojiButton.remove()
+
+    $votesBlock = @getVotesBlock()
+
+    if $votesBlock.find('.js-emoji-btn').length is 0
+      $votesBlock.addClass 'hidden'
+
+
+  getAwardTooltip: ($awardBlock) ->
+
+    return $awardBlock.attr('data-original-title') or $awardBlock.attr('data-title') or ''
+
+
+  removeMeFromUserList: ($emojiButton, emoji) ->
+
+    awardBlock    = $emojiButton
+    originalTitle = @getAwardTooltip awardBlock
+
+    authors = originalTitle.split ', '
+    authors.splice authors.indexOf('me'), 1
+
+    newAuthors = authors.join ', '
+
     awardBlock
-      .closest('.js-emoji-btn')
-      .attr('data-original-title', authors.join(', '))
-    @resetTooltip(awardBlock)
-
-  addMeToAuthorList: (emoji) ->
-    awardBlock = @findEmojiIcon(emoji).parent()
-    origTitle = awardBlock.attr('data-original-title').trim()
-    authors = []
+      .closest '.js-emoji-btn'
+      .removeData 'original-title'
+      .attr 'data-original-title', newAuthors
+
+    @resetTooltip awardBlock
+
+
+  addMeToUserList: (votesBlock, emoji) ->
+
+    awardBlock = @findEmojiIcon(votesBlock, emoji).parent()
+    origTitle  = @getAwardTooltip awardBlock
+    users      = []
+
     if origTitle
-      authors = origTitle.split(', ')
-    authors.push('me')
-    awardBlock.attr('data-original-title', authors.join(', '))
-    @resetTooltip(awardBlock)
+      users = origTitle.trim().split ', '
+
+    users.push 'me'
+    awardBlock.attr 'title', users.join ', '
+
+    @resetTooltip awardBlock
+
 
   resetTooltip: (award) ->
-    award.tooltip('destroy')
 
-    # "destroy" call is asynchronous and there is no appropriate callback on it, this is why we need to set timeout.
-    setTimeout (->
-      award.tooltip()
-    ), 200
+    award.tooltip 'destroy'
+
+    # 'destroy' call is asynchronous and there is no appropriate callback on it, this is why we need to set timeout.
+    cb = -> award.tooltip()
+    setTimeout cb, 200
+
 
+  createEmoji_: (votesBlock, emoji) ->
 
-  createEmoji: (emoji) ->
-    emojiCssClass = @resolveNameToCssClass(emoji)
+    emojiCssClass = @resolveNameToCssClass emoji
+    buttonHtml    = "<button class='btn award-control js-emoji-btn has-tooltip active' title='me' data-placement='bottom'>
+      <div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>
+      <span class='award-control-text js-counter'>1</span>
+    </button>"
 
-    nodes = []
-    nodes.push(
-      "<button class='btn award-control js-emoji-btn has-tooltip active' data-original-title='me'>",
-      "<div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>",
-      "<span class='award-control-text js-counter'>1</span>",
-      "</button>"
-    )
+    $emojiButton = $ buttonHtml
+    $emojiButton
+      .insertBefore votesBlock.find '.js-award-holder'
+      .find '.emoji-icon'
+      .data 'emoji', emoji
 
-    $(nodes.join("\n"))
-      .insertBefore('.js-award-holder')
-      .find('.emoji-icon')
-      .data('emoji', emoji)
+    @animateEmoji $emojiButton
     $('.award-control').tooltip()
+    votesBlock.removeClass 'current'
+
+
+  animateEmoji: ($emoji) ->
+
+    className = 'pulse animated'
+
+    $emoji.addClass className
+    setTimeout (-> $emoji.removeClass className), 321
+
+
+  createEmoji: (votesBlock, emoji) ->
+
+    if $('.emoji-menu').length
+      return @createEmoji_ votesBlock, emoji
+
+    @createEmojiMenu @getAwardMenuUrl(), => @createEmoji_ votesBlock, emoji
+
+
+  getAwardMenuUrl: -> return gon.award_menu_url
+
 
   resolveNameToCssClass: (emoji) ->
-    emojiIcon = $(".emoji-menu-content [data-emoji='#{emoji}']")
+
+    emojiIcon = $ ".emoji-menu-content [data-emoji='#{emoji}']"
 
     if emojiIcon.length > 0
-      unicodeName = emojiIcon.data('unicode-name')
+      unicodeName = emojiIcon.data 'unicode-name'
     else
       # Find by alias
-      unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data('unicode-name')
+      unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data 'unicode-name'
 
-    "emoji-#{unicodeName}"
+    return "emoji-#{unicodeName}"
 
-  postEmoji: (emoji, callback) ->
-    $.post @postEmojiUrl, { note: {
-      note: ":#{emoji}:"
-      noteable_type: @noteableType
-      noteable_id: @noteableId
-    }},(data) ->
-      if data.ok
-        callback.call()
 
-  findEmojiIcon: (emoji) ->
-    $(".awards > .js-emoji-btn [data-emoji='#{emoji}']")
+  postEmoji: (awardUrl, emoji, callback) ->
+
+    $.post awardUrl, { name: emoji }, (data) ->
+      callback() if data.ok
+
+
+  findEmojiIcon: (votesBlock, emoji) ->
+
+    return votesBlock.find ".js-emoji-btn [data-emoji='#{emoji}']"
+
 
   scrollToAwards: ->
-    $('body, html').animate({
-      scrollTop: $('.awards').offset().top - 80
-    }, 200)
+
+    options = scrollTop: $('.awards').offset().top - 110
+    $('body, html').animate options, 200
+
+
+  normilizeEmojiName: (emoji) -> return @aliases[emoji] or emoji
+
 
   addEmojiToFrequentlyUsedList: (emoji) ->
+
     frequentlyUsedEmojis = @getFrequentlyUsedEmojis()
-    frequentlyUsedEmojis.push(emoji)
-    $.cookie('frequently_used_emojis', frequentlyUsedEmojis.join(','), { expires: 365 })
+    frequentlyUsedEmojis.push emoji
+    $.cookie 'frequently_used_emojis', frequentlyUsedEmojis.join(','), { expires: 365 }
+
 
   getFrequentlyUsedEmojis: ->
-    frequentlyUsedEmojis = ($.cookie('frequently_used_emojis') || '').split(',')
-    _.compact(_.uniq(frequentlyUsedEmojis))
+
+    frequentlyUsedEmojis = ($.cookie('frequently_used_emojis') or '').split(',')
+    return _.compact _.uniq frequentlyUsedEmojis
+
 
   renderFrequentlyUsedBlock: ->
-    if $.cookie('frequently_used_emojis')
+
+    if $.cookie 'frequently_used_emojis'
       frequentlyUsedEmojis = @getFrequentlyUsedEmojis()
 
-      ul = $('<ul>')
+      ul = $("<ul class='clearfix emoji-menu-list frequent-emojis'>")
 
       for emoji in frequentlyUsedEmojis
-        do (emoji) ->
-          $(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul)
+        $(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul)
 
       $('input.emoji-search').after(ul).after($('<h5>').text('Frequently used'))
 
+    @frequentEmojiBlockRendered = true
+
+
   setupSearch: ->
-    $('input.emoji-search').keyup (ev) =>
+
+    $('input.emoji-search').on 'keyup', (ev) =>
       term = $(ev.target).val()
 
       # Clean previous search results
@@ -204,12 +357,14 @@ class @AwardsHandler
       if term
         # Generate a search result block
         h5 = $('<h5>').text('Search results').addClass('emoji-search')
-        foundEmojis = @searchEmojis(term).show()
-        ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(foundEmojis)
+        found_emojis = @searchEmojis(term).show()
+        ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis)
         $('.emoji-menu-content ul, .emoji-menu-content h5').hide()
         $('.emoji-menu-content').append(h5).append(ul)
       else
         $('.emoji-menu-content').children().show()
 
-  searchEmojis: (term)->
-    $(".emoji-menu-content [data-emoji*='#{term}']").closest("li").clone()
+
+  searchEmojis: (term) ->
+
+    $(".emoji-menu-list:not(.frequent-emojis) [data-emoji*='#{term}']").closest('li').clone()
diff --git a/app/assets/javascripts/blob/blob_gitignore_selector.js.coffee b/app/assets/javascripts/blob/blob_gitignore_selector.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..cc8a497d081537c3edee103f89480f9463248682
--- /dev/null
+++ b/app/assets/javascripts/blob/blob_gitignore_selector.js.coffee
@@ -0,0 +1,58 @@
+class @BlobGitignoreSelector
+  constructor: (opts) ->
+    {
+      @dropdown
+      @editor
+      @$wrapper        = @dropdown.closest('.gitignore-selector')
+      @$filenameInput  = $('#file_name')
+      @data           = @dropdown.data('filenames')
+    } = opts
+
+    @dropdown.glDropdown(
+      data: @data,
+      filterable: true,
+      selectable: true,
+      search:
+        fields: ['name']
+      clicked: @onClick
+      text: (gitignore) ->
+        gitignore.name
+    )
+
+    @toggleGitignoreSelector()
+    @bindEvents()
+
+  bindEvents: ->
+    @$filenameInput
+      .on 'keyup blur', (e) =>
+        @toggleGitignoreSelector()
+
+  toggleGitignoreSelector: ->
+    filename = @$filenameInput.val() or $('.editor-file-name').text().trim()
+    @$wrapper.toggleClass 'hidden', filename isnt '.gitignore'
+
+  onClick: (item, el, e) =>
+    e.preventDefault()
+    @requestIgnoreFile(item.name)
+
+  requestIgnoreFile: (name) ->
+    Api.gitignoreText name, @requestIgnoreFileSuccess.bind(@)
+
+  requestIgnoreFileSuccess: (gitignore) ->
+    @editor.setValue(gitignore.content, 1)
+    @editor.focus()
+
+class @BlobGitignoreSelectors
+  constructor: (opts) ->
+    {
+      @$dropdowns = $('.js-gitignore-selector')
+      @editor
+    } = opts
+
+    @$dropdowns.each (i, dropdown) =>
+      $dropdown = $(dropdown)
+
+      new BlobGitignoreSelector(
+        dropdown: $dropdown,
+        editor: @editor
+      )
diff --git a/app/assets/javascripts/blob/edit_blob.js.coffee b/app/assets/javascripts/blob/edit_blob.js.coffee
index eea9aa972ee33715cf217579640065095300462b..79141e768b8464ecc31ce6d6de8b2b13ba647628 100644
--- a/app/assets/javascripts/blob/edit_blob.js.coffee
+++ b/app/assets/javascripts/blob/edit_blob.js.coffee
@@ -13,6 +13,7 @@ class @EditBlob
 
     @initModePanesAndLinks()
     new BlobLicenseSelector(@editor)
+    new BlobGitignoreSelectors(editor: @editor)
 
   initModePanesAndLinks: ->
     @$editModePanes = $(".js-edit-mode-pane")
diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee
deleted file mode 100644
index d80e0e716cec7e85deb7d7aee0f4b0560153f82a..0000000000000000000000000000000000000000
--- a/app/assets/javascripts/calendar.js.coffee
+++ /dev/null
@@ -1,34 +0,0 @@
-class @Calendar
-  constructor: (timestamps, starting_year, starting_month, calendar_activities_path) ->
-    cal = new CalHeatMap()
-    cal.init
-      itemName: ["contribution"]
-      data: timestamps
-      start: new Date(starting_year, starting_month)
-      domainLabelFormat: "%b"
-      id: "cal-heatmap"
-      domain: "month"
-      subDomain: "day"
-      range: 12
-      tooltip: true
-      label:
-        position: "top"
-      legend: [
-        0
-        10
-        20
-        30
-      ]
-      legendCellPadding: 3
-      cellSize: $('.user-calendar').width() / 73
-      onClick: (date, count) ->
-        formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate()
-        $.ajax
-          url: calendar_activities_path
-          data:
-            date: formated_date
-          cache: false
-          dataType: "html"
-          success: (data) ->
-            $(".user-calendar-activities").html data
-
diff --git a/app/assets/javascripts/ci/build.coffee b/app/assets/javascripts/ci/build.coffee
index fca0c3bae5c40e6ee5496bb88950576e013951bf..2d515d7efa271213675f9e4f6892c67491d4249b 100644
--- a/app/assets/javascripts/ci/build.coffee
+++ b/app/assets/javascripts/ci/build.coffee
@@ -1,19 +1,33 @@
-class CiBuild
+class @CiBuild
   @interval: null
   @state: null
 
-  constructor: (build_url, build_status, build_state) ->
+  constructor: (@build_url, @build_status, @state) ->
     clearInterval(CiBuild.interval)
 
-    @state = build_state
+    # Init breakpoint checker
+    @bp = Breakpoints.get()
+    @hideSidebar()
+    $('.js-build-sidebar').niceScroll()
+    $(document)
+      .off 'click', '.js-sidebar-build-toggle'
+      .on 'click', '.js-sidebar-build-toggle', @toggleSidebar
 
-    @initScrollButtonAffix()
+    $(window)
+      .off 'resize.build'
+      .on 'resize.build', @hideSidebar
 
-    if build_status == "running" || build_status == "pending"
+    @updateArtifactRemoveDate()
+
+    if $('#build-trace').length
+      @getInitialBuildTrace()
+      @initScrollButtonAffix()
+
+    if @build_status is "running" or @build_status is "pending"
       #
       # Bind autoscroll button to follow build output
       #
-      $("#autoscroll-button").bind "click", ->
+      $('#autoscroll-button').on 'click', ->
         state = $(this).data("state")
         if "enabled" is state
           $(this).data "state", "disabled"
@@ -27,23 +41,37 @@ class CiBuild
       # Only valid for runnig build when output changes during time
       #
       CiBuild.interval = setInterval =>
-        if window.location.href.split("#").first() is build_url
-          $.ajax
-            url: build_url + "/trace.json?state=" + encodeURIComponent(@state)
-            dataType: "json"
-            success: (log) =>
-              @state = log.state
-              if log.status is "running"
-                if log.append
-                  $('.fa-refresh').before log.html
-                else
-                  $('#build-trace code').html log.html
-                  $('#build-trace code').append '<i class="fa fa-refresh fa-spin"/>'
-                @checkAutoscroll()
-              else if log.status isnt build_status
-                Turbolinks.visit build_url
+        if window.location.href.split("#").first() is @build_url
+          @getBuildTrace()
       , 4000
 
+  getInitialBuildTrace: ->
+    $.ajax
+      url: @build_url
+      dataType: 'json'
+      success: (build_data) ->
+        $('.js-build-output').html build_data.trace_html
+
+        if build_data.status is 'success' or build_data.status is 'failed'
+          $('.js-build-refresh').remove()
+
+  getBuildTrace: ->
+    $.ajax
+      url: "#{@build_url}/trace.json?state=#{encodeURIComponent(@state)}"
+      dataType: "json"
+      success: (log) =>
+        if log.state
+          @state = log.state
+
+        if log.status is "running"
+          if log.append
+            $('.js-build-output').append log.html
+          else
+            $('.js-build-output').html log.html
+          @checkAutoscroll()
+        else if log.status isnt @build_status
+          Turbolinks.visit @build_url
+
   checkAutoscroll: ->
     $("html,body").scrollTop $("#build-trace").height()  if "enabled" is $("#autoscroll-button").data("state")
 
@@ -58,4 +86,29 @@ class CiBuild
           $body.outerHeight() - ($buildTrace.outerHeight() + $buildTrace.offset().top)
     )
 
-@CiBuild = CiBuild
+  shouldHideSidebar: ->
+    bootstrapBreakpoint = @bp.getBreakpointSize()
+
+    bootstrapBreakpoint is 'xs' or bootstrapBreakpoint is 'sm'
+
+  toggleSidebar: =>
+    if @shouldHideSidebar()
+      $('.js-build-sidebar')
+        .toggleClass 'right-sidebar-expanded right-sidebar-collapsed'
+
+  hideSidebar: =>
+    if @shouldHideSidebar()
+      $('.js-build-sidebar')
+        .removeClass 'right-sidebar-expanded'
+        .addClass 'right-sidebar-collapsed'
+    else
+      $('.js-build-sidebar')
+        .removeClass 'right-sidebar-collapsed'
+        .addClass 'right-sidebar-expanded'
+
+  updateArtifactRemoveDate: ->
+    $date = $('.js-artifacts-remove')
+
+    if $date.length
+      date = $date.text()
+      $date.text $.timefor(new Date(date), ' ')
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index 9e6719d70c823916f5f8c49ee5c941b02943baa5..a032dd791755333d7215780327181777a0d909bf 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -16,8 +16,8 @@ class Dispatcher
     shortcut_handler = null
     switch page
       when 'projects:issues:index'
-        Issues.init()
         Issuable.init()
+        new IssuableBulkActions()
         shortcut_handler = new ShortcutsNavigation()
       when 'projects:issues:show'
         new Issue()
@@ -29,6 +29,7 @@ class Dispatcher
         new Todos()
       when 'projects:milestones:new', 'projects:milestones:edit'
         new ZenMode()
+        new DueDateSelect()
         new GLForm($('.milestone-form'))
       when 'groups:milestones:new'
         new ZenMode()
@@ -53,9 +54,13 @@ class Dispatcher
         new Diff()
         shortcut_handler = new ShortcutsIssuable(true)
         new ZenMode()
+        new MergedButtons()
+      when 'projects:merge_requests:commits', 'projects:merge_requests:builds'
+        new MergedButtons()
       when "projects:merge_requests:diffs"
         new Diff()
         new ZenMode()
+        new MergedButtons()
       when 'projects:merge_requests:index'
         shortcut_handler = new ShortcutsNavigation()
         Issuable.init()
@@ -68,9 +73,7 @@ class Dispatcher
         new Diff()
         new ZenMode()
         shortcut_handler = new ShortcutsNavigation()
-      when 'projects:commits:show'
-        shortcut_handler = new ShortcutsNavigation()
-      when 'projects:activity'
+      when 'projects:commits:show', 'projects:activity'
         shortcut_handler = new ShortcutsNavigation()
       when 'projects:show'
         shortcut_handler = new ShortcutsNavigation()
@@ -96,8 +99,11 @@ class Dispatcher
       when 'projects:blob:show', 'projects:blame:show'
         new LineHighlighter()
         shortcut_handler = new ShortcutsNavigation()
+        new ShortcutsBlob true
       when 'projects:labels:new', 'projects:labels:edit'
         new Labels()
+      when 'projects:labels:index'
+        new LabelManager() if $('.prioritized-labels').length
       when 'projects:network:show'
         # Ensure we don't create a particular shortcut handler here. This is
         # already created, where the network graph is created.
@@ -123,7 +129,7 @@ class Dispatcher
             new UsersSelect()
           when 'projects'
             new NamespaceSelect()
-      when 'dashboard'
+      when 'dashboard', 'root'
         shortcut_handler = new ShortcutsDashboardNavigation()
       when 'profiles'
         new Profile()
@@ -131,15 +137,11 @@ class Dispatcher
         new Project()
         new ProjectAvatar()
         switch path[1]
-          when 'compare'
-            shortcut_handler = new ShortcutsNavigation()
           when 'edit'
             shortcut_handler = new ShortcutsNavigation()
             new ProjectNew()
-          when 'new'
+          when 'new', 'show'
             new ProjectNew()
-          when 'show'
-            new ProjectShow()
           when 'wikis'
             new Wikis()
             shortcut_handler = new ShortcutsNavigation()
@@ -148,9 +150,9 @@ class Dispatcher
           when 'snippets'
             shortcut_handler = new ShortcutsNavigation()
             new ZenMode() if path[2] == 'show'
-          when 'labels', 'graphs'
-            shortcut_handler = new ShortcutsNavigation()
-          when 'project_members', 'deploy_keys', 'hooks', 'services', 'protected_branches'
+          when 'labels', 'graphs', 'compare', 'pipelines', 'forks', \
+          'milestones', 'project_members', 'deploy_keys', 'builds', \
+          'hooks', 'services', 'protected_branches'
             shortcut_handler = new ShortcutsNavigation()
 
     # If we haven't installed a custom shortcut handler, install the default one
diff --git a/app/assets/javascripts/due_date_select.js.coffee b/app/assets/javascripts/due_date_select.js.coffee
index a4304786cbb6de3f6f8bb6aa47e7b013e91ddef4..d65c018dad5f28537a540059ac75afeada86aa17 100644
--- a/app/assets/javascripts/due_date_select.js.coffee
+++ b/app/assets/javascripts/due_date_select.js.coffee
@@ -1,5 +1,21 @@
 class @DueDateSelect
   constructor: ->
+    # Milestone edit/new form
+    $datePicker = $('.datepicker')
+
+    if $datePicker.length
+      $dueDate = $('#milestone_due_date')
+      $datePicker.datepicker
+        dateFormat: 'yy-mm-dd'
+        onSelect: (dateText, inst) ->
+          $dueDate.val(dateText)
+      .datepicker('setDate', $.datepicker.parseDate('yy-mm-dd', $dueDate.val()))
+
+    $('.js-clear-due-date').on 'click', (e) ->
+      e.preventDefault()
+      $.datepicker._clearDate($datePicker)
+
+    # Issuable sidebar
     $loading = $('.js-issuable-update .due_date')
       .find('.block-loading')
       .hide()
@@ -11,6 +27,7 @@ class @DueDateSelect
       $block = $dropdown.closest('.block')
       $selectbox = $dropdown.closest('.selectbox')
       $value = $block.find('.value')
+      $valueContent = $block.find('.value-content')
       $sidebarValue = $('.js-due-date-sidebar-value', $block)
 
       fieldName = $dropdown.data('field-name')
@@ -20,14 +37,18 @@ class @DueDateSelect
       $dropdown.glDropdown(
         hidden: ->
           $selectbox.hide()
-          $value.removeAttr('style')
+          $value.css('display', '')
       )
 
-      addDueDate = ->
+      addDueDate = (isDropdown) ->
         # Create the post date
         value = $("input[name='#{fieldName}']").val()
-        date = new Date value.replace(new RegExp('-', 'g'), ',')
-        mediumDate = $.datepicker.formatDate 'M d, yy', date
+
+        if value isnt ''
+          date = new Date value.replace(new RegExp('-', 'g'), ',')
+          mediumDate = $.datepicker.formatDate 'M d, yy', date
+        else
+          mediumDate = 'No due date'
 
         data = {}
         data[abilityName] = {}
@@ -37,25 +58,39 @@ class @DueDateSelect
           type: 'PUT'
           url: issueUpdateURL
           data: data
+          dataType: 'json'
           beforeSend: ->
             $loading.fadeIn()
-            $dropdown.trigger('loading.gl.dropdown')
-            $selectbox.hide()
-            $value.removeAttr('style')
+            if isDropdown
+              $dropdown.trigger('loading.gl.dropdown')
+              $selectbox.hide()
+            $value.css('display', '')
 
-            $value.html(mediumDate)
+            cssClass = if Date.parse(mediumDate) then 'bold' else 'no-value'
+            $valueContent.html("<span class='#{cssClass}'>#{mediumDate}</span>")
             $sidebarValue.html(mediumDate)
+
+            if value isnt ''
+              $('.js-remove-due-date-holder').removeClass 'hidden'
+            else
+              $('.js-remove-due-date-holder').addClass 'hidden'
         ).done (data) ->
-          $dropdown.trigger('loaded.gl.dropdown')
-          $dropdown.dropdown('toggle')
+          if isDropdown
+            $dropdown.trigger('loaded.gl.dropdown')
+            $dropdown.dropdown('toggle')
           $loading.fadeOut()
 
+      $block.on 'click', '.js-remove-due-date', (e) ->
+        e.preventDefault()
+        $("input[name='#{fieldName}']").val ''
+        addDueDate(false)
+
       $datePicker.datepicker(
         dateFormat: 'yy-mm-dd',
         defaultDate: $("input[name='#{fieldName}']").val()
         altField: "input[name='#{fieldName}']"
         onSelect: ->
-          addDueDate()
+          addDueDate(true)
       )
 
     $(document)
diff --git a/app/assets/javascripts/flash.js.coffee b/app/assets/javascripts/flash.js.coffee
index 5de012e409f26d1a7dbf90069dd8c61cb2ecc201..4f73d215b8561a2a391a026bd177e99ca0fe8fd2 100644
--- a/app/assets/javascripts/flash.js.coffee
+++ b/app/assets/javascripts/flash.js.coffee
@@ -1,5 +1,5 @@
 class @Flash
-  constructor: (message, type)->
+  constructor: (message, type = 'alert')->
     @flash = $(".flash-container")
     @flash.html("")
 
diff --git a/app/assets/javascripts/gfm_auto_complete.js.coffee b/app/assets/javascripts/gfm_auto_complete.js.coffee
index 41dba342107fb3283abf54d08078f3a9086b5130..76c3083232becf0880cf9e649c7f79c692d68922 100644
--- a/app/assets/javascripts/gfm_auto_complete.js.coffee
+++ b/app/assets/javascripts/gfm_auto_complete.js.coffee
@@ -3,6 +3,7 @@
 window.GitLab ?= {}
 GitLab.GfmAutoComplete =
   dataLoading: false
+  dataLoaded: false
 
   dataSource: ''
 
@@ -22,6 +23,24 @@ GitLab.GfmAutoComplete =
   Milestones:
     template: '<li>${title}</li>'
 
+  Loading:
+    template: '<li><i class="fa fa-refresh fa-spin"></i> Loading...</li>'
+
+  DefaultOptions:
+    sorter: (query, items, searchKey) ->
+      return items if items[0].name? and items[0].name is 'loading'
+
+      $.fn.atwho.default.callbacks.sorter(query, items, searchKey)
+    filter: (query, data, searchKey) ->
+      return data if data[0] is 'loading'
+
+      $.fn.atwho.default.callbacks.filter(query, data, searchKey)
+    beforeInsert: (value) ->
+      if not GitLab.GfmAutoComplete.dataLoaded
+        @at
+      else
+        value
+
   # Add GFM auto-completion to all input fields, that accept GFM input.
   setup: (wrap) ->
     @input = $('.js-gfm-input')
@@ -53,18 +72,37 @@ GitLab.GfmAutoComplete =
     # Emoji
     @input.atwho
       at: ':'
-      displayTpl: @Emoji.template
+      displayTpl: (value) =>
+        if value.path?
+          @Emoji.template
+        else
+          @Loading.template
       insertTpl: ':${name}:'
+      data: ['loading']
+      callbacks:
+        sorter: @DefaultOptions.sorter
+        filter: @DefaultOptions.filter
+        beforeInsert: @DefaultOptions.beforeInsert
 
     # Team Members
     @input.atwho
       at: '@'
-      displayTpl: @Members.template
+      displayTpl: (value) =>
+        if value.username?
+          @Members.template
+        else
+          @Loading.template
       insertTpl: '${atwho-at}${username}'
       searchKey: 'search'
+      data: ['loading']
       callbacks:
+        sorter: @DefaultOptions.sorter
+        filter: @DefaultOptions.filter
+        beforeInsert: @DefaultOptions.beforeInsert
         beforeSave: (members) ->
           $.map members, (m) ->
+            return m if not m.username?
+
             title = m.name
             title += " (#{m.count})" if m.count
 
@@ -76,11 +114,21 @@ GitLab.GfmAutoComplete =
       at: '#'
       alias: 'issues'
       searchKey: 'search'
-      displayTpl: @Issues.template
+      displayTpl:  (value) =>
+        if value.title?
+          @Issues.template
+        else
+          @Loading.template
+      data: ['loading']
       insertTpl: '${atwho-at}${id}'
       callbacks:
+        sorter: @DefaultOptions.sorter
+        filter: @DefaultOptions.filter
+        beforeInsert: @DefaultOptions.beforeInsert
         beforeSave: (issues) ->
           $.map issues, (i) ->
+            return i if not i.title?
+
             id:     i.iid
             title:  sanitize(i.title)
             search: "#{i.iid} #{i.title}"
@@ -89,11 +137,18 @@ GitLab.GfmAutoComplete =
       at: '%'
       alias: 'milestones'
       searchKey: 'search'
-      displayTpl: @Milestones.template
+      displayTpl:  (value) =>
+        if value.title?
+          @Milestones.template
+        else
+          @Loading.template
       insertTpl: '${atwho-at}"${title}"'
+      data: ['loading']
       callbacks:
         beforeSave: (milestones) ->
           $.map milestones, (m) ->
+            return m if not m.title?
+
             id:     m.iid
             title:  sanitize(m.title)
             search: "#{m.title}"
@@ -102,11 +157,21 @@ GitLab.GfmAutoComplete =
       at: '!'
       alias: 'mergerequests'
       searchKey: 'search'
-      displayTpl: @Issues.template
+      displayTpl:  (value) =>
+        if value.title?
+          @Issues.template
+        else
+          @Loading.template
+      data: ['loading']
       insertTpl: '${atwho-at}${id}'
       callbacks:
+        sorter: @DefaultOptions.sorter
+        filter: @DefaultOptions.filter
+        beforeInsert: @DefaultOptions.beforeInsert
         beforeSave: (merges) ->
           $.map merges, (m) ->
+            return m if not m.title?
+
             id:     m.iid
             title:  sanitize(m.title)
             search: "#{m.iid} #{m.title}"
@@ -118,6 +183,8 @@ GitLab.GfmAutoComplete =
     $.getJSON(dataSource)
 
   loadData: (data) ->
+    @dataLoaded = true
+
     # load members
     @input.atwho 'load', '@', data.members
     # load issues
@@ -128,3 +195,7 @@ GitLab.GfmAutoComplete =
     @input.atwho 'load', 'mergerequests', data.mergerequests
     # load emojis
     @input.atwho 'load', ':', data.emojis
+
+    # This trigger at.js again
+    # otherwise we would be stuck with loading until the user types
+    $(':focus').trigger('keyup')
diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee
index 1d1bfeb2e7746f9f973712fe4f56d47eb47c2940..b49bd4565a7c75b11dbead10c80abb1d08815c7e 100644
--- a/app/assets/javascripts/gl_dropdown.js.coffee
+++ b/app/assets/javascripts/gl_dropdown.js.coffee
@@ -11,6 +11,8 @@ class GitLabDropdownFilter
     $inputContainer = @input.parent()
     $clearButton = $inputContainer.find('.js-dropdown-input-clear')
 
+    @indeterminateIds = []
+
     # Clear click
     $clearButton.on 'click', (e) =>
       e.preventDefault()
@@ -35,20 +37,20 @@ class GitLabDropdownFilter
       if keyCode is 13
         return false
 
-      clearTimeout timeout
-      timeout = setTimeout =>
-        blur_field = @shouldBlur keyCode
-        search_text = @input.val()
+      # Only filter asynchronously only if option remote is set
+      if @options.remote
+        clearTimeout timeout
+        timeout = setTimeout =>
+          blur_field = @shouldBlur keyCode
 
-        if blur_field and @filterInputBlur
-          @input.blur()
+          if blur_field and @filterInputBlur
+            @input.blur()
 
-        if @options.remote
-          @options.query search_text, (data) =>
+          @options.query @input.val(), (data) =>
             @options.callback(data)
-        else
-          @filter search_text
-      , 250
+        , 250
+      else
+        @filter @input.val()
 
   shouldBlur: (keyCode) ->
     return BLUR_KEYCODES.indexOf(keyCode) >= 0
@@ -60,9 +62,36 @@ class GitLabDropdownFilter
       results = data
 
       if search_text isnt ''
-        results = fuzzaldrinPlus.filter(data, search_text,
-          key: @options.keys
-        )
+        # When data is an array of objects therefore [object Array] e.g.
+        # [
+        #   { prop: 'foo' },
+        #   { prop: 'baz' }
+        # ]
+        if _.isArray(data)
+          results = fuzzaldrinPlus.filter(data, search_text,
+            key: @options.keys
+          )
+        else
+          # If data is grouped therefore an [object Object]. e.g.
+          # {
+          #   groupName1: [
+          #     { prop: 'foo' },
+          #     { prop: 'baz' }
+          #   ],
+          #   groupName2: [
+          #     { prop: 'abc' },
+          #     { prop: 'def' }
+          #   ]
+          # }
+          if gl.utils.isObject data
+            results = {}
+            for key, group of data
+              tmp = fuzzaldrinPlus.filter(group, search_text,
+                key: @options.keys
+              )
+
+              if tmp.length
+                results[key] = tmp.map (item) -> item
 
       @options.callback results
     else
@@ -115,6 +144,7 @@ class GitLabDropdown
   LOADING_CLASS = "is-loading"
   PAGE_TWO_CLASS = "is-page-two"
   ACTIVE_CLASS = "is-active"
+  INDETERMINATE_CLASS = "is-indeterminate"
   currentIndex = -1
 
   FILTER_INPUT = '.dropdown-input .dropdown-input-field'
@@ -141,8 +171,9 @@ class GitLabDropdown
     searchFields = if @options.search then @options.search.fields else [];
 
     if @options.data
-      # If data is an array
-      if _.isArray @options.data
+      # If we provided data
+      # data could be an array of objects or a group of arrays
+      if _.isObject(@options.data) and not _.isFunction(@options.data)
         @fullData = @options.data
         @parseData @options.data
       else
@@ -154,9 +185,6 @@ class GitLabDropdown
             @fullData = data
 
             @parseData @fullData
-
-            if @options.filterable
-              @filterInput.trigger 'keyup'
         }
 
     # Init filterable
@@ -183,6 +211,7 @@ class GitLabDropdown
 
     @dropdown.on "shown.bs.dropdown", @opened
     @dropdown.on "hidden.bs.dropdown", @hidden
+    $(@el).on "update.label", @updateLabel
     @dropdown.on "click", ".dropdown-menu, .dropdown-menu-close", @shouldPropagate
     @dropdown.on 'keyup', (e) =>
       if e.which is 27 # Escape key
@@ -230,19 +259,33 @@ class GitLabDropdown
   parseData: (data) ->
     @renderedData = data
 
-    # Render each row
-    html = $.map data, (obj) =>
-      return @renderItem(obj)
-
     if @options.filterable and data.length is 0
       # render no matching results
       html = [@noResults()]
+    else
+      # Handle array groups
+      if gl.utils.isObject data
+        html = []
+        for name, groupData of data
+          # Add header for each group
+          html.push(@renderItem(header: name, name))
+
+          @renderData(groupData, name)
+            .map (item) ->
+              html.push item
+      else
+        # Render each row
+        html = @renderData(data)
 
     # Render the full menu
     full_html = @renderMenu(html.join(""))
 
     @appendMenu(full_html)
 
+  renderData: (data, group = false) ->
+    data.map (obj, index) =>
+      return @renderItem(obj, group, index)
+
   shouldPropagate: (e) =>
     if @options.multiSelect
       $target = $(e.target)
@@ -256,6 +299,13 @@ class GitLabDropdown
   opened: =>
     @addArrowKeyEvent()
 
+    if @options.setIndeterminateIds
+      @options.setIndeterminateIds.call(@)
+
+    # Makes indeterminate items effective
+    if @fullData and @dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')
+      @parseData @fullData
+
     contentHtml = $('.dropdown-content', @dropdown).html()
     if @remote && contentHtml is ""
       @remote.execute()
@@ -267,12 +317,18 @@ class GitLabDropdown
 
   hidden: (e) =>
     @removeArrayKeyEvent()
+
+    $input = @dropdown.find(".dropdown-input-field")
+
     if @options.filterable
-      @dropdown
-        .find(".dropdown-input-field")
+      $input
         .blur()
         .val("")
-        .trigger("keyup")
+
+    # Triggering 'keyup' will re-render the dropdown which is not always required
+    # specially if we want to keep the state of the dropdown needed for bulk-assignment
+    if not @options.persistWhenHide
+      $input.trigger("keyup")
 
     if @dropdown.find(".dropdown-toggle-page").length
       $('.dropdown-menu', @dropdown).removeClass PAGE_TWO_CLASS
@@ -299,11 +355,10 @@ class GitLabDropdown
     selector = '.dropdown-content'
     if @dropdown.find(".dropdown-toggle-page").length
       selector = ".dropdown-page-one .dropdown-content"
-
     $(selector, @dropdown).html html
 
   # Render the row
-  renderItem: (data) ->
+  renderItem: (data, group = false, index = false) ->
     html = ""
 
     # Divider
@@ -317,7 +372,7 @@ class GitLabDropdown
 
     if @options.renderRow
       # Call the render function
-      html = @options.renderRow(data)
+      html = @options.renderRow.call(@options, data, @)
     else
       if not selected
         value = if @options.id then @options.id(data) else data.id
@@ -346,8 +401,13 @@ class GitLabDropdown
       if @highlight
         text = @highlightTextMatches(text, @filterInput.val())
 
+      if group
+        groupAttrs = "data-group='#{group}' data-index='#{index}'"
+      else
+        groupAttrs = ''
+
       html = "<li>
-        <a href='#{url}' class='#{cssClass}'>
+        <a href='#{url}' #{groupAttrs} class='#{cssClass}'>
           #{text}
         </a>
       </li>"
@@ -377,9 +437,15 @@ class GitLabDropdown
 
   rowClicked: (el) ->
     fieldName = @options.fieldName
-    selectedIndex = el.parent().index()
     if @renderedData
-      selectedObject = @renderedData[selectedIndex]
+      groupName = el.data('group')
+      if groupName
+        selectedIndex = el.data('index')
+        selectedObject = @renderedData[groupName][selectedIndex]
+      else
+        selectedIndex = el.closest('li').index()
+        selectedObject = @renderedData[selectedIndex]
+
     value = if @options.id then @options.id(selectedObject, el) else selectedObject.id
     field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']")
     if el.hasClass(ACTIVE_CLASS)
@@ -388,9 +454,20 @@ class GitLabDropdown
 
       # Toggle the dropdown label
       if @options.toggleLabel
-        $(@el).find(".dropdown-toggle-text").text @options.toggleLabel
+        @updateLabel()
       else
         selectedObject
+    else if el.hasClass(INDETERMINATE_CLASS)
+      el.addClass ACTIVE_CLASS
+      el.removeClass INDETERMINATE_CLASS
+
+      if not value?
+        field.remove()
+
+      if not field.length and fieldName
+        @addInput(fieldName, value)
+
+      return selectedObject
     else
       if not @options.multiSelect or el.hasClass('dropdown-clear-active')
         @dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS
@@ -404,34 +481,45 @@ class GitLabDropdown
 
       # Toggle the dropdown label
       if @options.toggleLabel
-        $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selectedObject, el)
+        @updateLabel(selectedObject, el)
       if value?
         if !field.length and fieldName
-          # Create hidden input for form
-          input = "<input type='hidden' name='#{fieldName}' value='#{value}' />"
-          if @options.inputId?
-            input = $(input)
-                      .attr('id', @options.inputId)
-          @dropdown.before input
+          @addInput(fieldName, value)
         else
           field.val value
 
       return selectedObject
 
-  selectRowAtIndex: (index) ->
-    selector = ".dropdown-content li:not(.divider):eq(#{index}) a"
+  addInput: (fieldName, value)->
+    # Create hidden input for form
+    $input = $('<input>').attr('type', 'hidden')
+                         .attr('name', fieldName)
+                        .val(value)
+
+    if @options.inputId?
+      $input.attr('id', @options.inputId)
+
+    @dropdown.before $input
+
+  selectRowAtIndex: (e, index) ->
+    selector = ".dropdown-content li:not(.divider,.dropdown-header,.separator):eq(#{index}) a"
 
     if @dropdown.find(".dropdown-toggle-page").length
       selector = ".dropdown-page-one #{selector}"
 
     # simulate a click on the first link
-    $(selector, @dropdown).trigger "click"
+    $el = $(selector, @dropdown)
+
+    if $el.length
+      e.preventDefault()
+      e.stopImmediatePropagation()
+      $(selector, @dropdown)[0].click()
 
   addArrowKeyEvent: ->
     ARROW_KEY_CODES = [38, 40]
     $input = @dropdown.find(".dropdown-input-field")
 
-    selector = '.dropdown-content li:not(.divider)'
+    selector = '.dropdown-content li:not(.divider,.dropdown-header,.separator)'
     if @dropdown.find(".dropdown-toggle-page").length
       selector = ".dropdown-page-one #{selector}"
 
@@ -459,8 +547,8 @@ class GitLabDropdown
 
         return false
 
-      if currentKeyCode is 13
-        @selectRowAtIndex currentIndex
+      if currentKeyCode is 13 and currentIndex isnt -1
+        @selectRowAtIndex e, currentIndex
 
   removeArrayKeyEvent: ->
     $('body').off 'keydown'
@@ -492,6 +580,9 @@ class GitLabDropdown
       # Scroll the dropdown content up
       $dropdownContent.scrollTop(listItemTop - dropdownContentTop)
 
+  updateLabel: (selected = null, el = null) =>
+    $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selected, el)
+
 $.fn.glDropdown = (opts) ->
   return @.each ->
     if (!$.data @, 'glDropdown')
diff --git a/app/assets/javascripts/graphs/application.js.coffee b/app/assets/javascripts/graphs/application.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..91f81a5d2490c0c40dd2c61d80bc2a8f4d306b97
--- /dev/null
+++ b/app/assets/javascripts/graphs/application.js.coffee
@@ -0,0 +1,8 @@
+# This is a manifest file that'll be compiled into including all the files listed below.
+# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+# be included in the compiled file accessible from http://example.com/assets/application.js
+# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
+# the compiled file.
+#
+#= require Chart
+#= require_tree .
diff --git a/app/assets/javascripts/stat_graph.js.coffee b/app/assets/javascripts/graphs/stat_graph.js.coffee
similarity index 100%
rename from app/assets/javascripts/stat_graph.js.coffee
rename to app/assets/javascripts/graphs/stat_graph.js.coffee
diff --git a/app/assets/javascripts/stat_graph_contributors.js.coffee b/app/assets/javascripts/graphs/stat_graph_contributors.js.coffee
similarity index 98%
rename from app/assets/javascripts/stat_graph_contributors.js.coffee
rename to app/assets/javascripts/graphs/stat_graph_contributors.js.coffee
index 3be14cb43dd504eefc81e438c6c15df9e514321f..1d9fae7cf79d81bbe66d4e6df1a56eb74b7d8e1f 100644
--- a/app/assets/javascripts/stat_graph_contributors.js.coffee
+++ b/app/assets/javascripts/graphs/stat_graph_contributors.js.coffee
@@ -1,5 +1,4 @@
 #= require d3
-#= require stat_graph_contributors_util
 
 class @ContributorsStatGraph
   init: (log) ->
diff --git a/app/assets/javascripts/stat_graph_contributors_graph.js.coffee b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js.coffee
similarity index 99%
rename from app/assets/javascripts/stat_graph_contributors_graph.js.coffee
rename to app/assets/javascripts/graphs/stat_graph_contributors_graph.js.coffee
index b7a0e073766ac02d674c73046fc2a396cf8a9539..584d281a510e06e2b4078938c3ade902b6f216ad 100644
--- a/app/assets/javascripts/stat_graph_contributors_graph.js.coffee
+++ b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js.coffee
@@ -1,6 +1,4 @@
 #= require d3
-#= require jquery
-#= require underscore
 
 class @ContributorsGraph
   MARGIN:
diff --git a/app/assets/javascripts/stat_graph_contributors_util.js.coffee b/app/assets/javascripts/graphs/stat_graph_contributors_util.js.coffee
similarity index 100%
rename from app/assets/javascripts/stat_graph_contributors_util.js.coffee
rename to app/assets/javascripts/graphs/stat_graph_contributors_util.js.coffee
diff --git a/app/assets/javascripts/issuable.js.coffee b/app/assets/javascripts/issuable.js.coffee
index afffed63ac57ec9c916111e7d9853040cbf18d46..d0901be1509eff7981e7592c67f14cc725106b30 100644
--- a/app/assets/javascripts/issuable.js.coffee
+++ b/app/assets/javascripts/issuable.js.coffee
@@ -1,13 +1,23 @@
+issuable_created = false
 @Issuable =
   init: ->
-    Issuable.initTemplates()
-    Issuable.initSearch()
+    unless issuable_created
+      issuable_created = true
+      Issuable.initTemplates()
+      Issuable.initSearch()
+      Issuable.initChecks()
+      Issuable.initLabelFilterRemove()
 
   initTemplates: ->
     Issuable.labelRow = _.template(
       '<% _.each(labels, function(label){ %>
-        <span class="label-row">
-          <a href="#"><span class="label color-label has-tooltip" style="background-color: <%= label.color %>; color: <%= label.text_color %>" title="<%= _.escape(label.description) %>" data-container="body"><%= _.escape(label.title) %></span></a>
+        <span class="label-row btn-group" role="group" aria-label="<%= _.escape(label.title) %>" style="color: <%= label.text_color %>;">
+          <a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%= label.color %>;" title="<%= _.escape(label.description) %>" data-container="body">
+            <%= _.escape(label.title) %>
+          </a>
+          <button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%= label.color %>;" data-label="<%= _.escape(label.title) %>">
+            <i class="fa fa-times"></i>
+          </button>
         </span>
       <% }); %>'
     )
@@ -19,15 +29,32 @@
       .on 'keyup', ->
         clearTimeout(@timer)
         @timer = setTimeout( ->
-          Issuable.filterResults $('#issue_search_form')
+          $search = $('#issue_search')
+          $form = $('.js-filter-form')
+          $input = $("input[name='#{$search.attr('name')}']", $form)
+
+          if $input.length is 0
+            $form.append "<input type='hidden' name='#{$search.attr('name')}' value='#{_.escape($search.val())}'/>"
+          else
+            $input.val $search.val()
+
+          Issuable.filterResults $form
         , 500)
 
-  toggleLabelFilters: ->
-    $filteredLabels = $('.filtered-labels')
-    if $filteredLabels.find('.label-row').length > 0
-      $filteredLabels.removeClass('hidden')
-    else
-      $filteredLabels.addClass('hidden')
+  initLabelFilterRemove: ->
+    $(document)
+      .off 'click', '.js-label-filter-remove'
+      .on 'click', '.js-label-filter-remove', (e) ->
+        $button = $(@)
+
+        # Remove the label input box
+        $('input[name="label_name[]"]')
+          .filter -> @value is $button.data('label')
+          .remove()
+
+        # Submit the form to get new data
+        Issuable.filterResults $('.filter-form')
+        $('.js-label-select').trigger('update.label')
 
   filterResults: (form) =>
     formData = form.serialize()
@@ -37,48 +64,27 @@
     issuesUrl = formAction
     issuesUrl += ("#{if formAction.indexOf('?') < 0 then '?' else '&'}")
     issuesUrl += formData
-    $.ajax
-      type: 'GET'
-      url: formAction
-      data: formData
-      complete: ->
-        $('.issues-holder, .merge-requests-holder').css('opacity', '1.0')
-      success: (data) ->
-        $('.issues-holder, .merge-requests-holder').html(data.html)
-        # Change url so if user reload a page - search results are saved
-        history.replaceState {page: issuesUrl}, document.title, issuesUrl
-        Issuable.reload()
-        Issuable.updateStateFilters()
-        $filteredLabels = $('.filtered-labels')
 
-        if typeof Issuable.labelRow is 'function'
-          $filteredLabels.html(Issuable.labelRow(data))
+    Turbolinks.visit(issuesUrl);
 
-        Issuable.toggleLabelFilters()
-
-      dataType: "json"
-
-  reload: ->
-    if Issues.created
-      Issues.initChecks()
-
-    $('#filter_issue_search').val($('#issue_search').val())
+  initChecks: ->
+    $('.check_all_issues').off('click').on('click', ->
+      $('.selected_issue').prop('checked', @checked)
+      Issuable.checkChanged()
+    )
 
-  updateStateFilters: ->
-    stateFilters =  $('.issues-state-filters')
-    newParams = {}
-    paramKeys = ['author_id', 'milestone_title', 'assignee_id', 'issue_search']
+    $('.selected_issue').off('change').on('change', Issuable.checkChanged)
 
-    for paramKey in paramKeys
-      newParams[paramKey] = gl.utils.getParameterValues(paramKey)[0] or ''
+  checkChanged: ->
+    checked_issues = $('.selected_issue:checked')
+    if checked_issues.length > 0
+      ids = $.map checked_issues, (value) ->
+        $(value).data('id')
 
-    if stateFilters.length
-      stateFilters.find('a').each ->
-        initialUrl = gl.utils.removeParamQueryString($(this).attr('href'), 'label_name[]')
-        labelNameValues = gl.utils.getParameterValues('label_name[]')
-        if labelNameValues
-          labelNameQueryString = ("label_name[]=#{value}" for value in labelNameValues).join('&')
-          newUrl = "#{gl.utils.mergeUrlParams(newParams, initialUrl)}&#{labelNameQueryString}"
-        else
-          newUrl = gl.utils.mergeUrlParams(newParams, initialUrl)
-        $(this).attr 'href', newUrl
+      $('#update_issues_ids').val ids
+      $('.issues-other-filters').hide()
+      $('.issues_bulk_update').show()
+    else
+      $('#update_issues_ids').val []
+      $('.issues_bulk_update').hide()
+      $('.issues-other-filters').show()
diff --git a/app/assets/javascripts/issuable_form.js.coffee b/app/assets/javascripts/issuable_form.js.coffee
index 7a788f761b7ac87493bf5d558297660bc4e77a81..5b7a4831dfc8fe1449b6649f9b8aedd6ea928dee 100644
--- a/app/assets/javascripts/issuable_form.js.coffee
+++ b/app/assets/javascripts/issuable_form.js.coffee
@@ -19,6 +19,16 @@ class @IssuableForm
     @form.on "click", ".btn-cancel", @resetAutosave
 
     @initWip()
+    @initMoveDropdown()
+
+    $issuableDueDate = $('#issuable-due-date')
+
+    if $issuableDueDate.length
+      $('.datepicker').datepicker(
+        dateFormat: 'yy-mm-dd',
+        onSelect: (dateText, inst) ->
+          $issuableDueDate.val dateText
+      ).datepicker 'setDate', $.datepicker.parseDate('yy-mm-dd', $issuableDueDate.val())
 
   initAutosave: ->
     new Autosave @titleField, [
@@ -80,3 +90,23 @@ class @IssuableForm
 
   addWip: ->
     @titleField.val "WIP: #{@titleField.val()}"
+
+  initMoveDropdown: ->
+    $moveDropdown = $('.js-move-dropdown')
+
+    if $moveDropdown.length
+      $('.js-move-dropdown').select2
+        ajax:
+          url: $moveDropdown.data('projects-url')
+          results: (data) ->
+            return {
+              results: data
+            }
+          data: (query) ->
+            {
+              search: query
+            }
+        formatResult: (project) ->
+          project.name_with_namespace
+        formatSelection: (project) ->
+          project.name_with_namespace
diff --git a/app/assets/javascripts/issues-bulk-assignment.js.coffee b/app/assets/javascripts/issues-bulk-assignment.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..b454f9389dd789326d75dafd26303ceed476d509
--- /dev/null
+++ b/app/assets/javascripts/issues-bulk-assignment.js.coffee
@@ -0,0 +1,121 @@
+class @IssuableBulkActions
+  constructor: (opts = {}) ->
+    # Set defaults
+    {
+      @container = $('.content')
+      @form = @getElement('.bulk-update')
+      @issues = @getElement('.issues-list .issue')
+    } = opts
+
+    @bindEvents()
+
+    # Fixes bulk-assign not working when navigating through pages
+    Issuable.initChecks();
+
+  getElement: (selector) ->
+    @container.find selector
+
+  bindEvents: ->
+    @form.off('submit').on('submit', @onFormSubmit.bind(@))
+
+  onFormSubmit: (e) ->
+    e.preventDefault()
+    @submit()
+
+  submit: ->
+    _this = @
+
+    xhr = $.ajax
+            url: @form.attr 'action'
+            method: @form.attr 'method'
+            dataType: 'JSON',
+            data: @getFormDataAsObject()
+
+    xhr.done (response, status, xhr) ->
+      location.reload()
+
+    xhr.fail ->
+      new Flash("Issue update failed")
+
+    xhr.always @onFormSubmitAlways.bind(@)
+
+  onFormSubmitAlways: ->
+    @form.find('[type="submit"]').enable()
+
+  getSelectedIssues: ->
+    @issues.has('.selected_issue:checked')
+
+  getLabelsFromSelection: ->
+    labels = []
+
+    @getSelectedIssues().map ->
+      _labels = $(@).data('labels')
+      if _labels
+        _labels.map (labelId) ->
+          labels.push(labelId) if labels.indexOf(labelId) is -1
+
+    labels
+
+  ###*
+   * Will return only labels that were marked previously and the user has unmarked
+   * @return {Array} Label IDs
+  ###
+  getUnmarkedIndeterminedLabels: ->
+    result = []
+    labelsToKeep = []
+
+    for el in @getElement('.labels-filter .is-indeterminate')
+      labelsToKeep.push $(el).data('labelId')
+
+    for id in @getLabelsFromSelection()
+      # Only the ones that we are not going to keep
+      result.push(id) if labelsToKeep.indexOf(id) is -1
+
+    result
+
+  ###*
+   * Simple form serialization, it will return just what we need
+   * Returns key/value pairs from form data
+  ###
+  getFormDataAsObject: ->
+    formData =
+      update:
+        state_event       : @form.find('input[name="update[state_event]"]').val()
+        assignee_id       : @form.find('input[name="update[assignee_id]"]').val()
+        milestone_id      : @form.find('input[name="update[milestone_id]"]').val()
+        issues_ids        : @form.find('input[name="update[issues_ids]"]').val()
+        add_label_ids     : []
+        remove_label_ids  : []
+
+    @getLabelsToApply().map (id) ->
+      formData.update.add_label_ids.push id
+
+    @getLabelsToRemove().map (id) ->
+      formData.update.remove_label_ids.push id
+
+    formData
+
+  getLabelsToApply: ->
+    labelIds = []
+    $labels = @form.find('.labels-filter input[name="update[label_ids][]"]')
+
+    $labels.each (k, label) ->
+      labelIds.push parseInt($(label).val()) if label
+
+    labelIds
+
+  ###*
+   * Returns Label IDs that will be removed from issue selection
+   * @return {Array} Array of labels IDs
+  ###
+  getLabelsToRemove: ->
+    result = []
+    indeterminatedLabels = @getUnmarkedIndeterminedLabels()
+    labelsToApply = @getLabelsToApply()
+
+    indeterminatedLabels.map (id) ->
+      # We need to exclude label IDs that will be applied
+      # By not doing this will cause issues from selection to not add labels at all
+      result.push(id) if labelsToApply.indexOf(id) is -1
+
+    result
diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee
deleted file mode 100644
index 3330e6c68aded1a471a45bbb9be51315e39db664..0000000000000000000000000000000000000000
--- a/app/assets/javascripts/issues.js.coffee
+++ /dev/null
@@ -1,38 +0,0 @@
-@Issues =
-  init: ->
-    Issues.created = true
-    Issues.initChecks()
-
-    $("body").on "ajax:success", ".close_issue, .reopen_issue", ->
-      t = $(this)
-      totalIssues = undefined
-      reopen = t.hasClass("reopen_issue")
-      $(".issue_counter").each ->
-        issue = $(this)
-        totalIssues = parseInt($(this).html(), 10)
-        if reopen and issue.closest(".main_menu").length
-          $(this).html totalIssues + 1
-        else
-          $(this).html totalIssues - 1
-
-  initChecks: ->
-    $(".check_all_issues").click ->
-      $(".selected_issue").prop("checked", @checked)
-      Issues.checkChanged()
-
-    $(".selected_issue").bind "change", Issues.checkChanged
-
-  checkChanged: ->
-    checked_issues = $(".selected_issue:checked")
-    if checked_issues.length > 0
-      ids = []
-      $.each checked_issues, (index, value) ->
-        ids.push $(value).attr("data-id")
-
-      $("#update_issues_ids").val ids
-      $(".issues-other-filters").hide()
-      $(".issues_bulk_update").show()
-    else
-      $("#update_issues_ids").val []
-      $(".issues_bulk_update").hide()
-      $(".issues-other-filters").show()
diff --git a/app/assets/javascripts/labels_select.js.coffee b/app/assets/javascripts/labels_select.js.coffee
index 995fd768603f92bc64f035cbb999e2f6c0c0f5d6..d350a7c0e7fb77754ef84f1f1660949b296f0308 100644
--- a/app/assets/javascripts/labels_select.js.coffee
+++ b/app/assets/javascripts/labels_select.js.coffee
@@ -1,5 +1,7 @@
 class @LabelsSelect
   constructor: ->
+    _this = @
+
     $('.js-label-select').each (i, dropdown) ->
       $dropdown = $(dropdown)
       projectId = $dropdown.data('project-id')
@@ -37,7 +39,7 @@ class @LabelsSelect
             </a>
             <% }); %>'
         )
-        labelNoneHTMLTemplate = _.template('<div class="light">None</div>')
+        labelNoneHTMLTemplate = '<span class="no-value">None</span>'
 
       if newLabelField.length
 
@@ -93,8 +95,11 @@ class @LabelsSelect
             $newLabelCreateButton.enable()
 
             if label.message?
+              errors = _.map label.message, (value, key) ->
+                "#{key} #{value[0]}"
+
               $newLabelError
-                .text label.message
+                .html errors.join("<br/>")
                 .show()
             else
               $('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
@@ -140,7 +145,7 @@ class @LabelsSelect
             template = labelHTMLTemplate(data)
             labelCount = data.labels.length
           else
-            template = labelNoneHTMLTemplate()
+            template = labelNoneHTMLTemplate
           $value
             .removeAttr('style')
             .html(template)
@@ -196,10 +201,18 @@ class @LabelsSelect
 
             callback data
 
-        renderRow: (label) ->
-          removesAll = label.id is 0 or not label.id?
+        renderRow: (label, instance) ->
+          $li = $('<li>')
+          $a  = $('<a href="#">')
 
           selectedClass = []
+          removesAll = label.id is 0 or not label.id?
+
+          if $dropdown.hasClass('js-filter-bulk-update')
+            indeterminate = instance.indeterminateIds
+            if indeterminate.indexOf(label.id) isnt -1
+              selectedClass.push 'is-indeterminate'
+
           if $form.find("input[type='hidden']\
             [name='#{$dropdown.data('fieldName')}']\
             [value='#{this.id(label)}']").length
@@ -230,17 +243,21 @@ class @LabelsSelect
           else
             colorEl = ''
 
-          "<li>
-            <a href='#' class='#{selectedClass.join(' ')}'>
-              #{colorEl}
-              #{_.escape(label.title)}
-            </a>
-          </li>"
-        filterable: true
+          # We need to identify which items are actually labels
+          if label.id
+            selectedClass.push('label-item')
+            $a.attr('data-label-id', label.id)
+
+          $a.addClass(selectedClass.join(' '))
+            .html("#{colorEl} #{_.escape(label.title)}")
+
+          # Return generated html
+          $li.html($a).prop('outerHTML')
+        persistWhenHide: $dropdown.data('persistWhenHide')
         search:
           fields: ['title']
         selectable: true
-
+        filterable: true
         toggleLabel: (selected, el) ->
           selected_labels = $('.js-label-select').siblings('.dropdown-menu-labels').find('.is-active')
 
@@ -280,10 +297,19 @@ class @LabelsSelect
             else if $dropdown.hasClass('js-filter-submit')
               $dropdown.closest('form').submit()
             else
-              saveLabelData()
+              if not $dropdown.hasClass 'js-filter-bulk-update'
+                saveLabelData()
+
+          if $dropdown.hasClass('js-filter-bulk-update')
+            # If we are persisting state we need the classes
+            if not @options.persistWhenHide
+              $dropdown.parent().find('.is-active, .is-indeterminate').removeClass()
 
         multiSelect: $dropdown.hasClass 'js-multiselect'
         clicked: (label) ->
+          if $dropdown.hasClass('js-filter-bulk-update')
+            return
+
           page = $('body').data 'page'
           isIssueIndex = page is 'projects:issues:index'
           isMRIndex = page is 'projects:merge_requests:index'
@@ -298,4 +324,31 @@ class @LabelsSelect
               return
             else
               saveLabelData()
+
+        setIndeterminateIds: ->
+          if @dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')
+            @indeterminateIds = _this.getIndeterminateIds()
       )
+
+    @bindEvents()
+
+  bindEvents: ->
+    $('body').on 'change', '.selected_issue', @onSelectCheckboxIssue
+
+  onSelectCheckboxIssue: ->
+    return if $('.selected_issue:checked').length
+
+    # Remove inputs
+    $('.issues_bulk_update .labels-filter input[type="hidden"]').remove()
+
+    # Also restore button text
+    $('.issues_bulk_update .labels-filter .dropdown-toggle-text').text('Label')
+
+  getIndeterminateIds: ->
+    label_ids = []
+
+    $('.selected_issue:checked').each (i, el) ->
+      issue_id = $(el).data('id')
+      label_ids.push $("#issue_#{issue_id}").data('labels')
+
+    _.flatten(label_ids)
diff --git a/app/assets/javascripts/layout_nav.js.coffee b/app/assets/javascripts/layout_nav.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..f8f0aea427e5bf10639c6eb620286795313d32df
--- /dev/null
+++ b/app/assets/javascripts/layout_nav.js.coffee
@@ -0,0 +1,25 @@
+hideEndFade = ($scrollingTabs) ->
+  $scrollingTabs.each ->
+    $this = $(@)
+
+    $this
+      .find('.fade-right')
+      .toggleClass('end-scroll', $this.width() is $this.prop('scrollWidth'))
+
+$ ->
+  $('.fade-left').addClass('end-scroll')
+
+  hideEndFade($('.scrolling-tabs'))
+
+  $(window)
+    .off 'resize.nav'
+    .on 'resize.nav', ->
+      hideEndFade($('.scrolling-tabs'))
+
+  $('.scrolling-tabs').on 'scroll', (event) ->
+    $this = $(this)
+    currentPosition = $this.scrollLeft()
+    maxPosition = $this.prop('scrollWidth') - $this.outerWidth()
+
+    $this.find('.fade-left').toggleClass('end-scroll', currentPosition is 0)
+    $this.find('.fade-right').toggleClass('end-scroll', currentPosition is maxPosition)
diff --git a/app/assets/javascripts/lib/common_utils.js.coffee b/app/assets/javascripts/lib/common_utils.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..73c7b04b626e3823de8caadd62d6fbaa47a152d2
--- /dev/null
+++ b/app/assets/javascripts/lib/common_utils.js.coffee
@@ -0,0 +1,68 @@
+((w) ->
+
+  w.gl       or= {}
+  w.gl.utils or= {}
+
+  w.gl.utils.isInGroupsPage = ->
+
+    return $('body').data('page').split(':')[0] is 'groups'
+
+
+  w.gl.utils.isInProjectPage = ->
+
+    return $('body').data('page').split(':')[0] is 'projects'
+
+
+  w.gl.utils.getProjectSlug = ->
+
+    return if @isInProjectPage() then $('body').data 'project' else null
+
+
+  w.gl.utils.getGroupSlug = ->
+
+    return if @isInGroupsPage() then $('body').data 'group' else null
+
+
+
+  gl.utils.updateTooltipTitle = ($tooltipEl, newTitle) ->
+
+    $tooltipEl
+      .tooltip 'destroy'
+      .attr    'title', newTitle
+      .tooltip 'fixTitle'
+
+
+  gl.utils.preventDisabledButtons = ->
+
+    $('.btn').click (e) ->
+      if $(this).hasClass 'disabled'
+        e.preventDefault()
+        e.stopImmediatePropagation()
+        return false
+  
+  gl.utils.capitalize = (str) ->
+    return str[0].toUpperCase() + str.slice(1);
+
+
+  jQuery.timefor = (time, suffix, expiredLabel) ->
+
+    return '' unless time
+
+    suffix       or= 'remaining'
+    expiredLabel or= 'Past due'
+
+    jQuery.timeago.settings.allowFuture = yes
+
+    { suffixFromNow } = jQuery.timeago.settings.strings
+    jQuery.timeago.settings.strings.suffixFromNow = suffix
+
+    timefor = $.timeago time
+
+    if timefor.indexOf('ago') > -1
+      timefor = expiredLabel
+
+    jQuery.timeago.settings.strings.suffixFromNow = suffixFromNow
+
+    return timefor
+
+) window
diff --git a/app/assets/javascripts/lib/datetime_utility.js.coffee b/app/assets/javascripts/lib/datetime_utility.js.coffee
index ad1d1c704819130185f350661ee8b7e2ce1002be..948d6dbf07ee4211f1338f836073d37fe934772c 100644
--- a/app/assets/javascripts/lib/datetime_utility.js.coffee
+++ b/app/assets/javascripts/lib/datetime_utility.js.coffee
@@ -12,6 +12,13 @@
           $el.attr('title', gl.utils.formatDate($el.attr('datetime')))
     )
 
-    $timeagoEls.timeago() if setTimeago
+    if setTimeago
+      $timeagoEls.timeago()
+      $timeagoEls.tooltip('destroy')
+
+      # Recreate with custom template
+      $timeagoEls.tooltip(
+        template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
+      )
 
 ) window
diff --git a/app/assets/javascripts/lib/emoji_aliases.js.coffee.erb b/app/assets/javascripts/lib/emoji_aliases.js.coffee.erb
new file mode 100644
index 0000000000000000000000000000000000000000..80f9936b9c2039ee8e1625128ad862e26d8847e0
--- /dev/null
+++ b/app/assets/javascripts/lib/emoji_aliases.js.coffee.erb
@@ -0,0 +1,2 @@
+gl.emojiAliases = ->
+  JSON.parse('<%= Gitlab::AwardEmoji.aliases.to_json %>')
diff --git a/app/assets/javascripts/lib/type_utility.js.coffee b/app/assets/javascripts/lib/type_utility.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..957f0d86b36ec7f8e5d59980ee69ecf0f18165d5
--- /dev/null
+++ b/app/assets/javascripts/lib/type_utility.js.coffee
@@ -0,0 +1,9 @@
+((w) ->
+
+  w.gl ?= {}
+  w.gl.utils ?= {}
+
+  w.gl.utils.isObject = (obj) ->
+    obj? and (obj.constructor is Object)
+
+) window
diff --git a/app/assets/javascripts/lib/url_utility.js.coffee b/app/assets/javascripts/lib/url_utility.js.coffee
index 6a00932c028ff23577a13f51c89efe348b7d3491..e8085e1c2e45b4ee4921c5123db3f623db2c6ab6 100644
--- a/app/assets/javascripts/lib/url_utility.js.coffee
+++ b/app/assets/javascripts/lib/url_utility.js.coffee
@@ -26,10 +26,19 @@
     newUrl = decodeURIComponent(url)
     for paramName, paramValue of params
       pattern = new RegExp "\\b(#{paramName}=).*?(&|$)"
-      if url.search(pattern) >= 0
+      if not paramValue?
+        newUrl = newUrl.replace pattern, ''
+      else if url.search(pattern) isnt -1
         newUrl = newUrl.replace pattern, "$1#{paramValue}$2"
       else
         newUrl = "#{newUrl}#{(if newUrl.indexOf('?') > 0 then '&' else '?')}#{paramName}=#{paramValue}"
+
+    # Remove a trailing ampersand
+    lastChar = newUrl[newUrl.length - 1]
+
+    if lastChar is '&'
+        newUrl = newUrl.slice 0, -1
+
     newUrl
 
   # removes parameter query string from url. returns the modified url
diff --git a/app/assets/javascripts/logo.js.coffee b/app/assets/javascripts/logo.js.coffee
index d14b7139237217a1cb1d6c7a5ea94dd3129378ee..dc2590a03557b23f2bb79da64f1530d0713a82bd 100644
--- a/app/assets/javascripts/logo.js.coffee
+++ b/app/assets/javascripts/logo.js.coffee
@@ -42,9 +42,3 @@ work = ->
 
 $(document).on('page:fetch',  start)
 $(document).on('page:change', stop)
-
-$ ->
-  # Make logo clickable as part of a workaround for Safari visited
-  # link behaviour (See !2690).
-  $('#logo').on 'click', ->
-    $('#js-shortcuts-home').get(0).click()
diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee
index 372732d0aac27ab6fed5ec47bf8cd75af3993843..49a4727205a6e9b53c3af3e460fd8a2f45ed06e1 100644
--- a/app/assets/javascripts/merge_request_tabs.js.coffee
+++ b/app/assets/javascripts/merge_request_tabs.js.coffee
@@ -75,6 +75,9 @@ class @MergeRequestTabs
       @loadDiff($target.attr('href'))
       if bp? and bp.getBreakpointSize() isnt 'lg'
         @shrinkView()
+
+      navBarHeight = $('.navbar-gitlab').outerHeight()
+      $.scrollTo(".merge-request-details .merge-request-tabs", offset: -navBarHeight)
     else if action == 'builds'
       @loadBuilds($target.attr('href'))
       @expandView()
diff --git a/app/assets/javascripts/merge_request_widget.js.coffee b/app/assets/javascripts/merge_request_widget.js.coffee
index 3b53b9d4384e3889d28dcb2886195732f8e2ebd0..77bf657ea0c14bd997fda5e2e6c84932377e6961 100644
--- a/app/assets/javascripts/merge_request_widget.js.coffee
+++ b/app/assets/javascripts/merge_request_widget.js.coffee
@@ -10,6 +10,7 @@ class @MergeRequestWidget
     $('#modal_merge_info').modal(show: false)
     @firstCICheck = true
     @readyForCICheck = false
+    @cancel = false
     clearInterval @fetchBuildStatusInterval
 
     @clearEventListeners()
@@ -21,10 +22,16 @@ class @MergeRequestWidget
   clearEventListeners: ->
     $(document).off 'page:change.merge_request'
 
+  cancelPolling: ->
+    @cancel = true
+
   addEventListeners: ->
+    allowedPages = ['show', 'commits', 'builds', 'changes']
     $(document).on 'page:change.merge_request', =>
-      if $('body').data('page') isnt 'projects:merge_requests:show'
+      page = $('body').data('page').split(':').last()
+      if allowedPages.indexOf(page) < 0
         clearInterval @fetchBuildStatusInterval
+        @cancelPolling()
         @clearEventListeners()
 
   mergeInProgress: (deleteSourceBranch = false)->
@@ -78,6 +85,7 @@ class @MergeRequestWidget
     $('.ci-widget-fetching').show()
 
     $.getJSON @opts.ci_status_url, (data) =>
+      return if @cancel
       @readyForCICheck = true
 
       if data.status is ''
@@ -117,6 +125,7 @@ class @MergeRequestWidget
         @firstCICheck = false
 
   showCIStatus: (state) ->
+    return if not state?
     $('.ci_widget').hide()
     allowed_states = ["failed", "canceled", "running", "pending", "success", "skipped", "not_found"]
     if state in allowed_states
@@ -124,7 +133,7 @@ class @MergeRequestWidget
       switch state
         when "failed", "canceled", "not_found"
           @setMergeButtonClass('btn-danger')
-        when "running", "pending"
+        when "running"
           @setMergeButtonClass('btn-warning')
         when "success"
           @setMergeButtonClass('btn-create')
@@ -137,6 +146,6 @@ class @MergeRequestWidget
     $('.ci_widget:visible .ci-coverage').text(text)
 
   setMergeButtonClass: (css_class) ->
-    $('.accept_merge_request')
+    $('.js-merge-button,.accept-action .dropdown-toggle')
       .removeClass('btn-danger btn-warning btn-create')
       .addClass(css_class)
diff --git a/app/assets/javascripts/merged_buttons.js.coffee b/app/assets/javascripts/merged_buttons.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..4929295c10b5baca7f81e8d872d4ec35a04c6b6d
--- /dev/null
+++ b/app/assets/javascripts/merged_buttons.js.coffee
@@ -0,0 +1,30 @@
+class @MergedButtons
+  constructor: ->
+    @$removeBranchWidget = $('.remove_source_branch_widget')
+    @$removeBranchProgress = $('.remove_source_branch_in_progress')
+    @$removeBranchFailed = $('.remove_source_branch_widget.failed')
+
+    @cleanEventListeners()
+    @initEventListeners()
+
+  cleanEventListeners: ->
+    $(document).off 'click', '.remove_source_branch'
+    $(document).off 'ajax:success', '.remove_source_branch'
+    $(document).off 'ajax:error', '.remove_source_branch'
+
+  initEventListeners: ->
+    $(document).on 'click', '.remove_source_branch', @removeSourceBranch
+    $(document).on 'ajax:success', '.remove_source_branch', @removeBranchSuccess
+    $(document).on 'ajax:error', '.remove_source_branch', @removeBranchError
+
+  removeSourceBranch: =>
+    @$removeBranchWidget.hide()
+    @$removeBranchProgress.show()
+
+  removeBranchSuccess: ->
+    location.reload()
+
+  removeBranchError: ->
+    @$removeBranchWidget.hide()
+    @$removeBranchProgress.hide()
+    @$removeBranchFailed.show()
diff --git a/app/assets/javascripts/milestone_select.js.coffee b/app/assets/javascripts/milestone_select.js.coffee
index 345a0e447af333a705d2c957d027daa93567e90b..02480f3a025408eae5427b0d0e2dbd5144b58b42 100644
--- a/app/assets/javascripts/milestone_select.js.coffee
+++ b/app/assets/javascripts/milestone_select.js.coffee
@@ -24,10 +24,16 @@ class @MilestoneSelect
 
       if issueUpdateURL
         milestoneLinkTemplate = _.template(
-          '<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>"><%= _.escape(title) %></a>'
+          '<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>" class="bold has-tooltip" data-container="body" title="<%= remaining %>"><%= _.escape(title) %></a>'
         )
 
-        milestoneLinkNoneTemplate = '<div class="light">None</div>'
+        milestoneLinkNoneTemplate = '<span class="no-value">None</span>'
+
+        collapsedSidebarLabelTemplate = _.template(
+          '<span class="has-tooltip" data-container="body" title="<%= remaining %>" data-placement="left">
+            <%= _.escape(title) %>
+          </span>'
+        )
 
       $dropdown.glDropdown(
         data: (term, callback) ->
@@ -83,7 +89,7 @@ class @MilestoneSelect
           $selectbox.hide()
 
           # display:block overrides the hide-collapse rule
-          $value.removeAttr('style')
+          $value.css('display', '')
         clicked: (selected) ->
           page = $('body').data 'page'
           isIssueIndex = page is 'projects:issues:index'
@@ -106,7 +112,7 @@ class @MilestoneSelect
               .val()
             data = {}
             data[abilityName] = {}
-            data[abilityName].milestone_id = selected
+            data[abilityName].milestone_id = if selected? then selected else null
             $loading
               .fadeIn()
             $dropdown.trigger('loading.gl.dropdown')
@@ -118,12 +124,13 @@ class @MilestoneSelect
               $dropdown.trigger('loaded.gl.dropdown')
               $loading.fadeOut()
               $selectbox.hide()
-              $value.removeAttr('style')
+              $value.css('display', '')
               if data.milestone?
                 data.milestone.namespace = _this.currentProject.namespace
                 data.milestone.path = _this.currentProject.path
+                data.milestone.remaining = $.timefor data.milestone.due_date
                 $value.html(milestoneLinkTemplate(data.milestone))
-                $sidebarCollapsedValue.find('span').text(data.milestone.title)
+                $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone))
               else
                 $value.html(milestoneLinkNoneTemplate)
                 $sidebarCollapsedValue.find('span').text('No')
diff --git a/app/assets/javascripts/network/application.js.coffee b/app/assets/javascripts/network/application.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..cb9eead855bed6f50c05fef7af6a5cdd69af4363
--- /dev/null
+++ b/app/assets/javascripts/network/application.js.coffee
@@ -0,0 +1,20 @@
+# This is a manifest file that'll be compiled into including all the files listed below.
+# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+# be included in the compiled file accessible from http://example.com/assets/application.js
+# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
+# the compiled file.
+#
+#= require raphael
+#= require g.raphael
+#= require g.bar
+#= require_tree .
+
+$ ->
+  network_graph = new Network({
+    url: $(".network-graph").attr('data-url'),
+    commit_url: $(".network-graph").attr('data-commit-url'),
+    ref: $(".network-graph").attr('data-ref'),
+    commit_id: $(".network-graph").attr('data-commit-id')
+  })
+
+  new ShortcutsNetwork(network_graph.branch_graph)
diff --git a/app/assets/javascripts/branch-graph.js.coffee b/app/assets/javascripts/network/branch-graph.js.coffee
similarity index 100%
rename from app/assets/javascripts/branch-graph.js.coffee
rename to app/assets/javascripts/network/branch-graph.js.coffee
diff --git a/app/assets/javascripts/network.js.coffee b/app/assets/javascripts/network/network.js.coffee
similarity index 100%
rename from app/assets/javascripts/network.js.coffee
rename to app/assets/javascripts/network/network.js.coffee
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index 6d9d6528f454dd18e34aebeeac00868c5581ec0a..e2d3241437b73b37000808aa5abeabf866c7b3d0 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -114,13 +114,15 @@ class @Notes
       @refresh()
     , @pollingInterval
 
-  refresh: ->
-    return if @refreshing is true
-    refreshing = true
+  refresh: =>
     if not document.hidden and document.URL.indexOf(@noteable_url) is 0
       @getContent()
 
   getContent: ->
+    return if @refreshing
+
+    @refreshing = true
+
     $.ajax
       url: @notes_url
       data: "last_fetched_at=" + @last_fetched_at
@@ -134,8 +136,8 @@ class @Notes
             @renderDiscussionNote(note)
           else
             @renderNote(note)
-      always: =>
-        @refreshing = false
+    .always () =>
+      @refreshing = false
 
   ###
   Increase @pollingInterval up to 120 seconds on every function call,
@@ -162,13 +164,14 @@ class @Notes
   renderNote: (note) ->
     unless note.valid
       if note.award
-        flash = new Flash('You have already used this award emoji!', 'alert')
+        flash = new Flash('You have already awarded this emoji!', 'alert')
         flash.pinTo('.header-content')
       return
 
     if note.award
-      awardsHandler.addAwardToEmojiBar(note.note)
-      awardsHandler.scrollToAwards()
+      votesBlock = $('.js-awards-block').eq 0
+      gl.awardsHandler.addAwardToEmojiBar votesBlock, note.name
+      gl.awardsHandler.scrollToAwards()
 
     # render note if it not present in loaded list
     # or skip if rendered
@@ -329,7 +332,7 @@ class @Notes
     @renderDiscussionNote(note)
 
     # cleanup after successfully creating a diff/discussion note
-    @removeDiscussionNoteForm($("#new-discussion-note-form-#{note.discussion_id}"))
+    @removeDiscussionNoteForm($(xhr.target))
 
   ###
   Called in response to the edit note form being submitted
@@ -353,8 +356,7 @@ class @Notes
   Called in response to clicking the edit note link
 
   Replaces the note text with the note edit form
-  Adds a hidden div with the original content of the note to fill the edit note form with
-  if the user cancels
+  Adds a data attribute to the form with the original content of the note for cancellations
   ###
   showEditForm: (e, scrollTo, myLastNote) ->
     e.preventDefault()
@@ -370,6 +372,8 @@ class @Notes
     done = ($noteText) ->
       # Neat little trick to put the cursor at the end
       noteTextVal = $noteText.val()
+      # Store the original note text in a data attribute to retrieve if a user cancels edit.
+      form.find('form.edit-note').data 'original-note', noteTextVal
       $noteText.val('').val(noteTextVal);
 
     new GLForm form
@@ -392,14 +396,16 @@ class @Notes
   ###
   Called in response to clicking the edit note link
 
-  Hides edit form
+  Hides edit form and restores the original note text to the editor textarea.
   ###
   cancelEdit: (e) ->
     e.preventDefault()
     note = $(this).closest(".note")
+    form = note.find(".current-note-edit-form")
     note.removeClass "is-editting"
-    note.find(".current-note-edit-form")
-      .removeClass("current-note-edit-form")
+    form.removeClass("current-note-edit-form")
+    # Replace markdown textarea text with original note text.
+    form.find(".js-note-text").val(form.find('form.edit-note').data('original-note'))
 
   ###
   Called in response to deleting a note of any kind.
diff --git a/app/assets/javascripts/pager.js.coffee b/app/assets/javascripts/pager.js.coffee
index 0ff83b7f0c85d3fa3ba96cf8ed578ab6bae99a1c..8049c5c30e2a2dcf275519be70bdfc68c9f66ae8 100644
--- a/app/assets/javascripts/pager.js.coffee
+++ b/app/assets/javascripts/pager.js.coffee
@@ -1,5 +1,5 @@
 @Pager =
-  init: (@limit = 0, preload, @disable = false) ->
+  init: (@limit = 0, preload, @disable = false, @callback = $.noop) ->
     @loading = $('.loading').first()
 
     if preload
@@ -19,6 +19,7 @@
         @loading.hide()
       success: (data) ->
         Pager.append(data.count, data.html)
+        Pager.callback()
       dataType: "json"
 
   append: (count, html) ->
diff --git a/app/assets/javascripts/path_locks.js.coffee b/app/assets/javascripts/path_locks.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..5f90f03daa54a0c80ae28b9b88bef2bf456b028d
--- /dev/null
+++ b/app/assets/javascripts/path_locks.js.coffee
@@ -0,0 +1,11 @@
+class @PathLocks
+  @init: (url, path) ->
+    $('.path-lock').on 'click',  ->
+      $lockBtn = $(this)
+      currentState = $lockBtn.data('state')
+      toggleAction = if currentState is 'lock' then 'unlock' else 'lock'
+      $.post url, {
+        path: path
+      }, ->
+        $lockBtn.text(gl.utils.capitalize(toggleAction))
+        $lockBtn.data('state', toggleAction)
diff --git a/app/assets/javascripts/project_new.js.coffee b/app/assets/javascripts/project_new.js.coffee
index 5349bda9e717af47160f8db91b467cd0c033f819..39dc108f7af20d6bffbbb03426d67717228efad1 100644
--- a/app/assets/javascripts/project_new.js.coffee
+++ b/app/assets/javascripts/project_new.js.coffee
@@ -7,24 +7,18 @@ class @ProjectNew
     @toggleSettingsOnclick()
 
 
-  toggleSettings: ->
-    checked = $("#project_merge_requests_enabled").prop("checked")
-    if checked
-      $('.merge-request-feature').show()
-    else
-      $('.merge-request-feature').hide()
-    checked = $("#project_issues_enabled").prop("checked")
-    if checked
-      $('.issues-feature').show()
-    else
-      $('.issues-feature').hide()
-    checked = $("#project_builds_enabled").prop("checked")
-    if checked
-      $('.builds-feature').show()
-    else
-      $('.builds-feature').hide()
+  toggleSettings: =>
+    @_showOrHide('#project_builds_enabled', '.builds-feature')
+    @_showOrHide('#project_merge_requests_enabled', '.merge-requests-feature')
+    @_showOrHide('#project_issues_enabled', '.issues-feature')
 
   toggleSettingsOnclick: ->
-    $("#project_merge_requests_enabled").on 'click', @toggleSettings
-    $("#project_issues_enabled").on 'click', @toggleSettings
-    $("#project_builds_enabled").on 'click', @toggleSettings
+    $('#project_builds_enabled, #project_merge_requests_enabled, #project_issues_enabled').on 'click', @toggleSettings
+
+  _showOrHide: (checkElement, container) ->
+    $container = $(container)
+
+    if $(checkElement).prop('checked')
+      $container.show()
+    else
+      $container.hide()
diff --git a/app/assets/javascripts/right_sidebar.js.coffee b/app/assets/javascripts/right_sidebar.js.coffee
index 2d084b76cfe1f4542419e064f0940bcfcc20fcfa..8eb005b0a22fdccd338bad67f9baeac0278b82ca 100644
--- a/app/assets/javascripts/right_sidebar.js.coffee
+++ b/app/assets/javascripts/right_sidebar.js.coffee
@@ -10,6 +10,89 @@ class @Sidebar
     $('.dropdown').on('loading.gl.dropdown', @sidebarDropdownLoading)
     $('.dropdown').on('loaded.gl.dropdown', @sidebarDropdownLoaded)
 
+
+    $(document)
+      .off 'click', '.js-sidebar-toggle'
+      .on 'click', '.js-sidebar-toggle', (e, triggered) ->
+        e.preventDefault()
+        $this = $(this)
+        $thisIcon = $this.find 'i'
+        $allGutterToggleIcons = $('.js-sidebar-toggle i')
+        if $thisIcon.hasClass('fa-angle-double-right')
+          $allGutterToggleIcons
+            .removeClass('fa-angle-double-right')
+            .addClass('fa-angle-double-left')
+          $('aside.right-sidebar')
+            .removeClass('right-sidebar-expanded')
+            .addClass('right-sidebar-collapsed')
+          $('.page-with-sidebar')
+            .removeClass('right-sidebar-expanded')
+            .addClass('right-sidebar-collapsed')
+        else
+          $allGutterToggleIcons
+            .removeClass('fa-angle-double-left')
+            .addClass('fa-angle-double-right')
+          $('aside.right-sidebar')
+            .removeClass('right-sidebar-collapsed')
+            .addClass('right-sidebar-expanded')
+          $('.page-with-sidebar')
+            .removeClass('right-sidebar-collapsed')
+            .addClass('right-sidebar-expanded')
+        if not triggered
+          $.cookie("collapsed_gutter",
+            $('.right-sidebar')
+              .hasClass('right-sidebar-collapsed'), { path: '/' })
+
+    $(document)
+      .off 'click', '.js-issuable-todo'
+      .on 'click', '.js-issuable-todo', @toggleTodo
+
+  toggleTodo: (e) =>
+    $this = $(e.currentTarget)
+    $todoLoading = $('.js-issuable-todo-loading')
+    $btnText = $('.js-issuable-todo-text', $this)
+    ajaxType = if $this.attr('data-id') then 'PATCH' else 'POST'
+    ajaxUrlExtra = if $this.attr('data-id') then "/#{$this.attr('data-id')}" else ''
+
+    $.ajax(
+      url: "#{$this.data('url')}#{ajaxUrlExtra}"
+      type: ajaxType
+      dataType: 'json'
+      data:
+        issuable_id: $this.data('issuable')
+        issuable_type: $this.data('issuable-type')
+      beforeSend: =>
+        @beforeTodoSend($this, $todoLoading)
+    ).done (data) =>
+      @todoUpdateDone(data, $this, $btnText, $todoLoading)
+
+  beforeTodoSend: ($btn, $todoLoading) ->
+    $btn.disable()
+    $todoLoading.removeClass 'hidden'
+
+  todoUpdateDone: (data, $btn, $btnText, $todoLoading) ->
+    $todoPendingCount = $('.todos-pending-count')
+    $todoPendingCount.text data.count
+
+    $btn.enable()
+    $todoLoading.addClass 'hidden'
+
+    if data.count is 0
+      $todoPendingCount.addClass 'hidden'
+    else
+      $todoPendingCount.removeClass 'hidden'
+
+    if data.todo?
+      $btn
+        .attr 'aria-label', $btn.data('mark-text')
+        .attr 'data-id', data.todo.id
+      $btnText.text $btn.data('mark-text')
+    else
+      $btn
+        .attr 'aria-label', $btn.data('todo-text')
+        .removeAttr 'data-id'
+      $btnText.text $btn.data('todo-text')
+
   sidebarDropdownLoading: (e) ->
     $sidebarCollapsedIcon = $(@).closest('.block').find('.sidebar-collapsed-icon')
     img = $sidebarCollapsedIcon.find('img')
@@ -76,12 +159,10 @@ class @Sidebar
       @triggerOpenSidebar() if not @isOpen()
 
     if action is 'hide'
-      @triggerOpenSidebar() is @isOpen()
+      @triggerOpenSidebar() if @isOpen()
 
   isOpen: ->
     @sidebar.is('.right-sidebar-expanded')
 
   getBlock: (name) ->
     @sidebar.find(".block.#{name}")
-
-
diff --git a/app/assets/javascripts/search_autocomplete.js.coffee b/app/assets/javascripts/search_autocomplete.js.coffee
index 6a7b4ad1db7f1598d41602fb5adcaed00041d2a9..421328554b8d6c156537784493969087fc2b2d63 100644
--- a/app/assets/javascripts/search_autocomplete.js.coffee
+++ b/app/assets/javascripts/search_autocomplete.js.coffee
@@ -20,8 +20,7 @@ class @SearchAutocomplete
     @dropdown = @wrap.find('.dropdown')
     @dropdownContent = @dropdown.find('.dropdown-content')
 
-    @locationBadgeEl = @getElement('.search-location-badge')
-    @locationText = @getElement('.location-text')
+    @locationBadgeEl = @getElement('.location-badge')
     @scopeInputEl = @getElement('#scope')
     @searchInput = @getElement('.search-input')
     @projectInputEl = @getElement('#search_project_id')
@@ -68,8 +67,12 @@ class @SearchAutocomplete
   getData: (term, callback) ->
     _this = @
 
-    # Do not trigger request if input is empty
-    return if @searchInput.val() is ''
+    unless term
+      if contents = @getCategoryContents()
+        @searchInput.data('glDropdown').filter.options.callback contents
+        @enableAutocomplete()
+
+      return
 
     # Prevent multiple ajax calls
     return if @loadingSuggestions
@@ -123,6 +126,37 @@ class @SearchAutocomplete
     ).always ->
       _this.loadingSuggestions = false
 
+
+  getCategoryContents: ->
+
+    userId = gon.current_user_id
+    { utils, projectOptions, groupOptions, dashboardOptions } = gl
+
+    if utils.isInGroupsPage() and groupOptions
+      options = groupOptions[utils.getGroupSlug()]
+
+    else if utils.isInProjectPage() and projectOptions
+      options = projectOptions[utils.getProjectSlug()]
+
+    else if dashboardOptions
+      options = dashboardOptions
+
+    { issuesPath, mrPath, name } = options
+
+    items = [
+      { header: "#{name}" }
+      { text: 'Issues assigned to me', url: "#{issuesPath}/?assignee_id=#{userId}" }
+      { text: "Issues I've created",   url: "#{issuesPath}/?author_id=#{userId}"   }
+      'separator'
+      { text: 'Merge requests assigned to me', url: "#{mrPath}/?assignee_id=#{userId}" }
+      { text: "Merge requests I've created",   url: "#{mrPath}/?author_id=#{userId}"   }
+    ]
+
+    items.splice 0, 1 unless name
+
+    return items
+
+
   serializeState: ->
     {
       # Search Criteria
@@ -133,7 +167,7 @@ class @SearchAutocomplete
       scope: @scopeInputEl.val()
 
       # Location badge
-      _location: @locationText.text()
+      _location: @locationBadgeEl.text()
     }
 
   bindEvents: ->
@@ -143,23 +177,28 @@ class @SearchAutocomplete
     @searchInput.on 'click', @onSearchInputClick
     @searchInput.on 'focus', @onSearchInputFocus
     @clearInput.on 'click', @onClearInputClick
+    @locationBadgeEl.on 'click', =>
+      @searchInput.focus()
 
   onDocumentClick: (e) =>
     # If clicking outside the search box
     # And search input is not focused
     # And we are not clicking inside a suggestion
-    if not $.contains(@dropdown[0], e.target) and @isFocused and not $(e.target).parents('ul').length
+    if not $.contains(@dropdown[0], e.target) and @isFocused and not $(e.target).closest('.search-form').length
       @onSearchInputBlur()
 
   enableAutocomplete: ->
     # No need to enable anything if user is not logged in
     return if !gon.current_user_id
 
-    _this = @
-    @loadingSuggestions = false
+    unless @dropdown.hasClass('open')
+      _this = @
+      @loadingSuggestions = false
 
-    @dropdown.addClass('open')
-    @searchInput.removeClass('disabled')
+      @dropdown
+        .addClass('open')
+        .trigger('shown.bs.dropdown')
+      @searchInput.removeClass('disabled')
 
   onSearchInputKeyDown: =>
     # Saves last length of the entered text
@@ -190,7 +229,7 @@ class @SearchAutocomplete
           @disableAutocomplete()
         else
           # We should display the menu only when input is not empty
-          @enableAutocomplete()
+          @enableAutocomplete() if e.keyCode isnt KEYCODE.ENTER
 
     @wrap.toggleClass 'has-value', !!e.target.value
 
@@ -205,6 +244,12 @@ class @SearchAutocomplete
     @isFocused = true
     @wrap.addClass('search-active')
 
+    @getData()  if @getValue() is ''
+
+
+  getValue: -> return @searchInput.val()
+
+
   onClearInputClick: (e) =>
     e.preventDefault()
     @searchInput.val('').focus()
@@ -221,21 +266,22 @@ class @SearchAutocomplete
     category = if item.category? then "#{item.category}: " else ''
     value = if item.value? then item.value else ''
 
-    html = "<span class='location-badge'>
-              <i class='location-text'>#{category}#{value}</i>
-            </span>"
-    @locationBadgeEl.html(html)
+    badgeText = "#{category}#{value}"
+    @locationBadgeEl.text(badgeText).show()
     @wrap.addClass('has-location-badge')
 
+
+  hasLocationBadge: -> return @wrap.is '.has-location-badge'
+
+
   restoreOriginalState: ->
     inputs = Object.keys @originalState
 
     for input in inputs
       @getElement("##{input}").val(@originalState[input])
 
-
     if @originalState._location is ''
-      @locationBadgeEl.empty()
+      @locationBadgeEl.hide()
     else
       @addLocationBadge(
         value: @originalState._location
@@ -244,7 +290,7 @@ class @SearchAutocomplete
     @dropdown.removeClass 'open'
 
   badgePresent: ->
-    @locationBadgeEl.children().length
+    @locationBadgeEl.length
 
   resetSearchState: ->
     inputs = Object.keys @originalState
@@ -256,13 +302,14 @@ class @SearchAutocomplete
 
       @getElement("##{input}").val('')
 
+
   removeLocationBadge: ->
-    @locationBadgeEl.empty()
 
-    # Reset state
+    @locationBadgeEl.hide()
     @resetSearchState()
-
     @wrap.removeClass('has-location-badge')
+    @disableAutocomplete()
+
 
   disableAutocomplete: ->
     @searchInput.addClass('disabled')
diff --git a/app/assets/javascripts/shortcuts.js.coffee b/app/assets/javascripts/shortcuts.js.coffee
index f3d66004138cd98d64642e74757fb0d1b20e377d..c03877e9b0697a6d5fe86d154505dee6361db98f 100644
--- a/app/assets/javascripts/shortcuts.js.coffee
+++ b/app/assets/javascripts/shortcuts.js.coffee
@@ -1,7 +1,7 @@
 class @Shortcuts
-  constructor: ->
+  constructor: (skipResetBindings) ->
     @enabledHelp = []
-    Mousetrap.reset()
+    Mousetrap.reset() if not skipResetBindings
     Mousetrap.bind('?', @onToggleHelp)
     Mousetrap.bind('s', Shortcuts.focusSearch)
     Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview)
diff --git a/app/assets/javascripts/shortcuts_blob.coffee b/app/assets/javascripts/shortcuts_blob.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..6d21e5d115023803648b275edc7686d5a9ff3caa
--- /dev/null
+++ b/app/assets/javascripts/shortcuts_blob.coffee
@@ -0,0 +1,10 @@
+#= require shortcuts
+
+class @ShortcutsBlob extends Shortcuts
+  constructor: (skipResetBindings) ->
+    super skipResetBindings
+    Mousetrap.bind('y', ShortcutsBlob.copyToClipboard)
+
+  @copyToClipboard: ->
+    clipboardButton = $('.btn-clipboard')
+    clipboardButton.click() if clipboardButton
diff --git a/app/assets/javascripts/shortcuts_dashboard_navigation.js.coffee b/app/assets/javascripts/shortcuts_dashboard_navigation.js.coffee
index 4a05bdccdb36888d52dbabcc94b323180259da90..cca2b8a1fccadfe79f41c2ed8895b3e2e5cdacfd 100644
--- a/app/assets/javascripts/shortcuts_dashboard_navigation.js.coffee
+++ b/app/assets/javascripts/shortcuts_dashboard_navigation.js.coffee
@@ -3,10 +3,10 @@
 class @ShortcutsDashboardNavigation extends Shortcuts
  constructor: ->
    super()
-   Mousetrap.bind('g a', -> ShortcutsDashboardNavigation.findAndFollowLink('.shortcuts-activity'))
-   Mousetrap.bind('g i', -> ShortcutsDashboardNavigation.findAndFollowLink('.shortcuts-issues'))
-   Mousetrap.bind('g m', -> ShortcutsDashboardNavigation.findAndFollowLink('.shortcuts-merge_requests'))
-   Mousetrap.bind('g p', -> ShortcutsDashboardNavigation.findAndFollowLink('.shortcuts-projects'))
+   Mousetrap.bind('g a', -> ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-activity'))
+   Mousetrap.bind('g i', -> ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-issues'))
+   Mousetrap.bind('g m', -> ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-merge_requests'))
+   Mousetrap.bind('g p', -> ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-projects'))
 
  @findAndFollowLink: (selector) ->
    link = $(selector).attr('href')
diff --git a/app/assets/javascripts/shortcuts_issuable.coffee b/app/assets/javascripts/shortcuts_issuable.coffee
index ccb42ab21683518d2c58ebbf9d6aad482462bc61..c93bcf3ceec4f7f6c566eaa2e92285792d9ef967 100644
--- a/app/assets/javascripts/shortcuts_issuable.coffee
+++ b/app/assets/javascripts/shortcuts_issuable.coffee
@@ -10,14 +10,6 @@ class @ShortcutsIssuable extends ShortcutsNavigation
       @replyWithSelectedText()
       return false
     )
-    Mousetrap.bind('j', =>
-      @prevIssue()
-      return false
-    )
-    Mousetrap.bind('k', =>
-      @nextIssue()
-      return false
-    )
     Mousetrap.bind('e', =>
       @editIssue()
       return false
@@ -29,16 +21,6 @@ class @ShortcutsIssuable extends ShortcutsNavigation
     else
       @enabledHelp.push('.hidden-shortcut.issues')
 
-  prevIssue: ->
-    $prevBtn = $('.prev-btn')
-    if not $prevBtn.hasClass('disabled')
-      Turbolinks.visit($prevBtn.attr('href'))
-
-  nextIssue: ->
-    $nextBtn = $('.next-btn')
-    if not $nextBtn.hasClass('disabled')
-      Turbolinks.visit($nextBtn.attr('href'))
-
   replyWithSelectedText: ->
     if window.getSelection
       selected = window.getSelection().toString()
diff --git a/app/assets/javascripts/sidebar.js.coffee b/app/assets/javascripts/sidebar.js.coffee
index ea4ac52da31cfc891e7678594391a09ef2188d79..68009e586452cfdbce7adf185bfc6bed39289d2a 100644
--- a/app/assets/javascripts/sidebar.js.coffee
+++ b/app/assets/javascripts/sidebar.js.coffee
@@ -3,24 +3,35 @@ expanded = 'page-sidebar-expanded'
 
 toggleSidebar = ->
   $('.page-with-sidebar').toggleClass("#{collapsed} #{expanded}")
-  $('header').toggleClass("header-collapsed header-expanded")
-  $('.toggle-nav-collapse i').toggleClass("fa-angle-right fa-angle-left")
-  $.cookie("collapsed_nav", $('.page-with-sidebar').hasClass(collapsed), { path: '/' })
+  $('.navbar-fixed-top').toggleClass("header-collapsed header-expanded")
+
+  if $.cookie('pin_nav') is 'true'
+    $('.navbar-fixed-top').toggleClass('header-pinned-nav')
+    $('.page-with-sidebar').toggleClass('page-sidebar-pinned')
 
   setTimeout ( ->
-    niceScrollBars = $('.nicescroll').niceScroll();
+    niceScrollBars = $('.nav-sidebar').niceScroll();
     niceScrollBars.updateScrollBar();
   ), 300
 
+$(document)
+  .off 'click', 'body'
+  .on 'click', 'body', (e) ->
+    unless $.cookie('pin_nav') is 'true'
+      $target = $(e.target)
+      $nav = $target.closest('.sidebar-wrapper')
+      pageExpanded = $('.page-with-sidebar').hasClass('page-sidebar-expanded')
+      $toggle = $target.closest('.toggle-nav-collapse, .side-nav-toggle')
+
+      if $nav.length is 0 and pageExpanded and $toggle.length is 0
+        $('.page-with-sidebar')
+          .toggleClass('page-sidebar-collapsed page-sidebar-expanded')
+
+        $('.navbar-fixed-top')
+          .toggleClass('header-collapsed header-expanded')
+
 $(document).on("click", '.toggle-nav-collapse, .side-nav-toggle', (e) ->
   e.preventDefault()
 
   toggleSidebar()
 )
-
-$ ->
-  size = bp.getBreakpointSize()
-
-  if size is "xs" or size is "sm"
-    if $('.page-with-sidebar').hasClass(expanded)
-      toggleSidebar()
diff --git a/app/assets/javascripts/star.js.coffee b/app/assets/javascripts/star.js.coffee
index f27780dda931d712facd77389375280e2f761e2b..01b28171f72218bac33288f2acd07a330a2039f8 100644
--- a/app/assets/javascripts/star.js.coffee
+++ b/app/assets/javascripts/star.js.coffee
@@ -9,9 +9,11 @@ class @Star
         $this.parent().find('.star-count').text data.star_count
         if isStarred
           $starSpan.removeClass('starred').text 'Star'
+          gl.utils.updateTooltipTitle $this, 'Star project'
           $starIcon.removeClass('fa-star').addClass 'fa-star-o'
         else
           $starSpan.addClass('starred').text 'Unstar'
+          gl.utils.updateTooltipTitle $this, 'Unstar project'
           $starIcon.removeClass('fa-star-o').addClass 'fa-star'
         return
 
diff --git a/app/assets/javascripts/subscription.js.coffee b/app/assets/javascripts/subscription.js.coffee
index 1a430f3aa47437ce1b50760ab27bc903896f4f79..08d494aba9fdb2129c9a4327b46882ee5f4be178 100644
--- a/app/assets/javascripts/subscription.js.coffee
+++ b/app/assets/javascripts/subscription.js.coffee
@@ -19,3 +19,8 @@ class @Subscription
       action = if status == 'subscribed' then 'Unsubscribe' else 'Subscribe'
       btn.find('span').text(action)
       @subscription_status.find('>div').toggleClass('hidden')
+
+      if btn.attr('data-original-title')
+        btn.tooltip('hide')
+          .attr('data-original-title', action)
+          .tooltip('fixTitle')
diff --git a/app/assets/javascripts/u2f/authenticate.js.coffee b/app/assets/javascripts/u2f/authenticate.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..6deb902c8de6d7c84f3ab17fd9da7e11e8bf08fc
--- /dev/null
+++ b/app/assets/javascripts/u2f/authenticate.js.coffee
@@ -0,0 +1,63 @@
+# Authenticate U2F (universal 2nd factor) devices for users to authenticate with.
+#
+# State Flow #1: setup -> in_progress -> authenticated -> POST to server
+# State Flow #2: setup -> in_progress -> error -> setup
+
+class @U2FAuthenticate
+  constructor: (@container, u2fParams) ->
+    @appId = u2fParams.app_id
+    @challenges = u2fParams.challenges
+    @signRequests = u2fParams.sign_requests
+
+  start: () =>
+    if U2FUtil.isU2FSupported()
+      @renderSetup()
+    else
+      @renderNotSupported()
+
+  authenticate: () =>
+    u2f.sign(@appId, @challenges, @signRequests, (response) =>
+      if response.errorCode
+        error = new U2FError(response.errorCode)
+        @renderError(error);
+      else
+        @renderAuthenticated(JSON.stringify(response))
+    , 10)
+
+  #############
+  # Rendering #
+  #############
+
+  templates: {
+    "notSupported": "#js-authenticate-u2f-not-supported",
+    "setup": '#js-authenticate-u2f-setup',
+    "inProgress": '#js-authenticate-u2f-in-progress',
+    "error": '#js-authenticate-u2f-error',
+    "authenticated": '#js-authenticate-u2f-authenticated'
+  }
+
+  renderTemplate: (name, params) =>
+    templateString = $(@templates[name]).html()
+    template = _.template(templateString)
+    @container.html(template(params))
+
+  renderSetup: () =>
+    @renderTemplate('setup')
+    @container.find('#js-login-u2f-device').on('click', @renderInProgress)
+
+  renderInProgress: () =>
+    @renderTemplate('inProgress')
+    @authenticate()
+
+  renderError: (error) =>
+    @renderTemplate('error', {error_message: error.message()})
+    @container.find('#js-u2f-try-again').on('click', @renderSetup)
+
+  renderAuthenticated: (deviceResponse) =>
+    @renderTemplate('authenticated')
+    # Prefer to do this instead of interpolating using Underscore templates
+    # because of JSON escaping issues.
+    @container.find("#js-device-response").val(deviceResponse)
+
+  renderNotSupported: () =>
+    @renderTemplate('notSupported')
diff --git a/app/assets/javascripts/u2f/error.js.coffee b/app/assets/javascripts/u2f/error.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..1a2fc3e757f4183f28d297022781273ca6da7139
--- /dev/null
+++ b/app/assets/javascripts/u2f/error.js.coffee
@@ -0,0 +1,13 @@
+class @U2FError
+  constructor: (@errorCode) ->
+    @httpsDisabled = (window.location.protocol isnt 'https:')
+    console.error("U2F Error Code: #{@errorCode}")
+
+  message: () =>
+    switch
+      when (@errorCode is u2f.ErrorCodes.BAD_REQUEST and @httpsDisabled)
+        "U2F only works with HTTPS-enabled websites. Contact your administrator for more details."
+      when @errorCode is u2f.ErrorCodes.DEVICE_INELIGIBLE
+        "This device has already been registered with us."
+      else
+        "There was a problem communicating with your device."
diff --git a/app/assets/javascripts/u2f/register.js.coffee b/app/assets/javascripts/u2f/register.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..74472cfa1208724727fb463b4fc3c2125ec9df37
--- /dev/null
+++ b/app/assets/javascripts/u2f/register.js.coffee
@@ -0,0 +1,63 @@
+# Register U2F (universal 2nd factor) devices for users to authenticate with.
+#
+# State Flow #1: setup -> in_progress -> registered -> POST to server
+# State Flow #2: setup -> in_progress -> error -> setup
+
+class @U2FRegister
+  constructor: (@container, u2fParams) ->
+    @appId = u2fParams.app_id
+    @registerRequests = u2fParams.register_requests
+    @signRequests = u2fParams.sign_requests
+
+  start: () =>
+    if U2FUtil.isU2FSupported()
+      @renderSetup()
+    else
+      @renderNotSupported()
+
+  register: () =>
+    u2f.register(@appId, @registerRequests, @signRequests, (response) =>
+      if response.errorCode
+        error = new U2FError(response.errorCode)
+        @renderError(error);
+      else
+        @renderRegistered(JSON.stringify(response))
+    , 10)
+
+  #############
+  # Rendering #
+  #############
+
+  templates: {
+    "notSupported": "#js-register-u2f-not-supported",
+    "setup": '#js-register-u2f-setup',
+    "inProgress": '#js-register-u2f-in-progress',
+    "error": '#js-register-u2f-error',
+    "registered": '#js-register-u2f-registered'
+  }
+
+  renderTemplate: (name, params) =>
+    templateString = $(@templates[name]).html()
+    template = _.template(templateString)
+    @container.html(template(params))
+
+  renderSetup: () =>
+    @renderTemplate('setup')
+    @container.find('#js-setup-u2f-device').on('click', @renderInProgress)
+
+  renderInProgress: () =>
+    @renderTemplate('inProgress')
+    @register()
+
+  renderError: (error) =>
+    @renderTemplate('error', {error_message: error.message()})
+    @container.find('#js-u2f-try-again').on('click', @renderSetup)
+
+  renderRegistered: (deviceResponse) =>
+    @renderTemplate('registered')
+    # Prefer to do this instead of interpolating using Underscore templates
+    # because of JSON escaping issues.
+    @container.find("#js-device-response").val(deviceResponse)
+
+  renderNotSupported: () =>
+    @renderTemplate('notSupported')
diff --git a/app/assets/javascripts/u2f/util.js.coffee.erb b/app/assets/javascripts/u2f/util.js.coffee.erb
new file mode 100644
index 0000000000000000000000000000000000000000..d59341c38b917ef39f699d120cbe96ed5af668ad
--- /dev/null
+++ b/app/assets/javascripts/u2f/util.js.coffee.erb
@@ -0,0 +1,15 @@
+# Helper class for U2F (universal 2nd factor) device registration and authentication.
+
+class @U2FUtil
+  @isU2FSupported: ->
+    if @testMode
+      true
+    else
+      gon.u2f.browser_supports_u2f
+
+  @enableTestMode: ->
+    @testMode = true
+
+<% if Rails.env.test? %>
+U2FUtil.enableTestMode();
+<% end %>
diff --git a/app/assets/javascripts/user_tabs.js.coffee b/app/assets/javascripts/user_tabs.js.coffee
index 70614396a4e561173090e548a178a6d387a243e8..29dad21faed5611589a425a8eb08e3497b8f8866 100644
--- a/app/assets/javascripts/user_tabs.js.coffee
+++ b/app/assets/javascripts/user_tabs.js.coffee
@@ -122,6 +122,9 @@ class @UserTabs
         @parentEl.find(tabSelector).html(data.html)
         @loaded[action] = true
 
+        # Fix tooltips
+        gl.utils.localTimeAgo($('.js-timeago', tabSelector))
+
   loadActivities: (source) ->
     return if @loaded['activity'] is true
 
diff --git a/app/assets/javascripts/users/application.js.coffee b/app/assets/javascripts/users/application.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..647ffbf5f45f53ef2a11be07272c7ccc835d2265
--- /dev/null
+++ b/app/assets/javascripts/users/application.js.coffee
@@ -0,0 +1,8 @@
+# This is a manifest file that'll be compiled into including all the files listed below.
+# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+# be included in the compiled file accessible from http://example.com/assets/application.js
+# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
+# the compiled file.
+#
+#= require d3
+#= require_tree .
diff --git a/app/assets/javascripts/users/calendar.js.coffee b/app/assets/javascripts/users/calendar.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..26a260615390767dc405b819d5937a94f3e3b388
--- /dev/null
+++ b/app/assets/javascripts/users/calendar.js.coffee
@@ -0,0 +1,198 @@
+class @Calendar
+  constructor: (timestamps, @calendar_activities_path) ->
+    @currentSelectedDate = ''
+    @daySpace = 1
+    @daySize = 15
+    @daySizeWithSpace = @daySize + (@daySpace * 2)
+    @monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+    @months = []
+    @highestValue = 0
+
+    # Get the highest value from the timestampes
+    _.each timestamps, (count) =>
+      if count > @highestValue
+        @highestValue = count
+
+    # Loop through the timestamps to create a group of objects
+    # The group of objects will be grouped based on the day of the week they are
+    @timestampsTmp = []
+    i = 0
+    group = 0
+    _.each timestamps, (count, date) =>
+      newDate = new Date parseInt(date) * 1000
+      day = newDate.getDay()
+
+      # Create a new group array if this is the first day of the week
+      # or if is first object
+      if (day is 0 and i isnt 0) or i is 0
+        @timestampsTmp.push []
+        group++
+
+      innerArray = @timestampsTmp[group-1]
+
+      # Push to the inner array the values that will be used to render map
+      innerArray.push
+        count: count
+        date: newDate
+        day: day
+
+      i++
+
+    # Init color functions
+    @color = @initColor()
+    @colorKey = @initColorKey()
+
+    # Init the svg element
+    @renderSvg(group)
+    @renderDays()
+    @renderMonths()
+    @renderDayTitles()
+    @renderKey()
+
+    @initTooltips()
+
+  renderSvg: (group) ->
+    @svg = d3.select '.js-contrib-calendar'
+      .append 'svg'
+      .attr 'width', (group + 1) * @daySizeWithSpace
+      .attr 'height', 167
+      .attr 'class', 'contrib-calendar'
+
+  renderDays: ->
+    @svg.selectAll 'g'
+      .data @timestampsTmp
+      .enter()
+      .append 'g'
+      .attr 'transform', (group, i) =>
+        _.each group, (stamp, a) =>
+          if a is 0 and stamp.day is 0
+            month = stamp.date.getMonth()
+            x = (@daySizeWithSpace * i + 1) + @daySizeWithSpace
+            lastMonth = _.last(@months)
+            if lastMonth?
+              lastMonthX = lastMonth.x
+
+            if !lastMonth?
+              @months.push
+                month: month
+                x: x
+            else if month isnt lastMonth.month and x - @daySizeWithSpace isnt lastMonthX
+              @months.push
+                month: month
+                x: x
+
+        "translate(#{(@daySizeWithSpace * i + 1) + @daySizeWithSpace}, 18)"
+      .selectAll 'rect'
+      .data (stamp) ->
+        stamp
+      .enter()
+      .append 'rect'
+      .attr 'x', '0'
+      .attr 'y', (stamp, i) =>
+        (@daySizeWithSpace * stamp.day)
+      .attr 'width', @daySize
+      .attr 'height', @daySize
+      .attr 'title', (stamp) =>
+        contribText = 'No contributions'
+
+        if stamp.count > 0
+          contribText = "#{stamp.count} contribution#{if stamp.count > 1 then 's' else ''}"
+
+        date = dateFormat(stamp.date, 'mmm d, yyyy')
+
+        "#{contribText}<br />#{date}"
+      .attr 'class', 'user-contrib-cell js-tooltip'
+      .attr 'fill', (stamp) =>
+        if stamp.count isnt 0
+          @color(stamp.count)
+        else
+          '#ededed'
+      .attr 'data-container', 'body'
+      .on 'click', @clickDay
+
+  renderDayTitles: ->
+    days = [{
+      text: 'M'
+      y: 29 + (@daySizeWithSpace * 1)
+    }, {
+      text: 'W'
+      y: 29 + (@daySizeWithSpace * 3)
+    }, {
+      text: 'F'
+      y: 29 + (@daySizeWithSpace * 5)
+    }]
+    @svg.append 'g'
+      .selectAll 'text'
+      .data days
+      .enter()
+      .append 'text'
+      .attr 'text-anchor', 'middle'
+      .attr 'x', 8
+      .attr 'y', (day) ->
+        day.y
+      .text (day) ->
+        day.text
+      .attr 'class', 'user-contrib-text'
+
+  renderMonths: ->
+    @svg.append 'g'
+      .selectAll 'text'
+      .data @months
+      .enter()
+      .append 'text'
+      .attr 'x', (date) ->
+        date.x
+      .attr 'y', 10
+      .attr 'class', 'user-contrib-text'
+      .text (date) =>
+        @monthNames[date.month]
+
+  renderKey: ->
+    keyColors = ['#ededed', @colorKey(0), @colorKey(1), @colorKey(2), @colorKey(3)]
+    @svg.append 'g'
+      .attr 'transform', "translate(18, #{@daySizeWithSpace * 8 + 16})"
+      .selectAll 'rect'
+      .data keyColors
+      .enter()
+      .append 'rect'
+      .attr 'width', @daySize
+      .attr 'height', @daySize
+      .attr 'x', (color, i) =>
+        @daySizeWithSpace * i
+      .attr 'y', 0
+      .attr 'fill', (color) ->
+        color
+
+  initColor: ->
+    d3.scale
+      .linear()
+      .range(['#acd5f2', '#254e77'])
+      .domain([0, @highestValue])
+
+  initColorKey: ->
+    d3.scale
+      .linear()
+      .range(['#acd5f2', '#254e77'])
+      .domain([0, 3])
+
+  clickDay: (stamp) =>
+    if @currentSelectedDate isnt stamp.date
+      @currentSelectedDate = stamp.date
+      formatted_date = @currentSelectedDate.getFullYear() + "-" + (@currentSelectedDate.getMonth()+1) + "-" + @currentSelectedDate.getDate()
+
+      $.ajax
+        url: @calendar_activities_path
+        data:
+          date: formatted_date
+        cache: false
+        dataType: 'html'
+        beforeSend: ->
+          $('.user-calendar-activities').html '<div class="text-center"><i class="fa fa-spinner fa-spin user-calendar-activities-loading"></i></div>'
+        success: (data) ->
+          $('.user-calendar-activities').html data
+    else
+      $('.user-calendar-activities').html ''
+
+  initTooltips: ->
+    $('.js-contrib-calendar .js-tooltip').tooltip
+      html: true
diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee
index 41099cff710b66613e5f04956141739f78f8cc6b..0408bf82c7f783ff2e3038ff918112114c7a005b 100644
--- a/app/assets/javascripts/users_select.js.coffee
+++ b/app/assets/javascripts/users_select.js.coffee
@@ -31,7 +31,7 @@ class @UsersSelect
       assignTo = (selected) ->
         data = {}
         data[abilityName] = {}
-        data[abilityName].assignee_id = selected
+        data[abilityName].assignee_id = if selected? then selected else null
         $loading
           .fadeIn()
         $dropdown.trigger('loading.gl.dropdown')
@@ -72,7 +72,7 @@ class @UsersSelect
 
       assigneeTemplate = _.template(
         '<% if (username) { %>
-        <a class="author_link " href="/u/<%= username %>">
+        <a class="author_link bold" href="/u/<%= username %>">
           <% if( avatar ) { %>
           <img width="32" class="avatar avatar-inline s32" alt="" src="<%= avatar %>">
           <% } %>
@@ -82,7 +82,7 @@ class @UsersSelect
           </span>
         </a>
           <% } else { %>
-        <span class="assign-yourself">
+        <span class="no-value assign-yourself">
           No assignee -
           <a href="#" class="js-assign-yourself">
             assign yourself
@@ -93,6 +93,8 @@ class @UsersSelect
 
       $dropdown.glDropdown(
         data: (term, callback) =>
+          isAuthorFilter = $('.js-author-search')
+
           @users term, (users) =>
             if term.length is 0
               showDivider = 0
@@ -138,7 +140,7 @@ class @UsersSelect
 
         toggleLabel: (selected) ->
           if selected && 'id' of selected
-            selected.name
+            if selected.text then selected.text else selected.name
           else
             defaultLabel
 
@@ -147,7 +149,7 @@ class @UsersSelect
         hidden: (e) ->
           $selectbox.hide()
           # display:block overrides the hide-collapse rule
-          $value.removeAttr('style')
+          $value.css('display', '')
 
         clicked: (user) ->
           page = $('body').data 'page'
diff --git a/app/assets/javascripts/weight_select.js.coffee b/app/assets/javascripts/weight_select.js.coffee
index 12fe9a94c61004592988ed6baf7d2bba913f610c..ee25780025673a4956c1d92dc22574ff1699e982 100644
--- a/app/assets/javascripts/weight_select.js.coffee
+++ b/app/assets/javascripts/weight_select.js.coffee
@@ -13,7 +13,7 @@ class @WeightSelect
       updateWeight = (selected) ->
         data = {}
         data[abilityName] = {}
-        data[abilityName].weight = selected
+        data[abilityName].weight = if selected? then selected else null
         $loading
           .fadeIn()
         $dropdown.trigger('loading.gl.dropdown')
@@ -27,7 +27,10 @@ class @WeightSelect
           $loading.fadeOut()
           $selectbox.hide()
 
-          $value.html(data.weight)
+          if data.weight?
+            $value.html(data.weight)
+          else
+            $value.html('None')
           $sidebarCollapsedValue.html(data.weight)
 
       $dropdown.glDropdown(
@@ -36,9 +39,10 @@ class @WeightSelect
         hidden: (e) ->
           $selectbox.hide()
           # display:block overrides the hide-collapse rule
-          $value.removeAttr('style')
+          $value.css('display', '')
         id: (obj, el) ->
-          $(el).data "id"
+          if not $(el).data("none")?
+            $(el).data "id"
         clicked: (selected) ->
           if $(dropdown).is ".js-filter-submit"
             $(dropdown).parents('form').submit()
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index e2d590f4df4e8bd37ffeb1952adc6f7d2f4b0f0e..8b93665d085eb8a5e7360588fe633d9b21d659eb 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -8,7 +8,6 @@
  *= require select2
  *= require_self
  *= require dropzone/basic
- *= require cal-heatmap
  *= require cropper.css
 */
 
diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index f5ce70b606b04767185baf093246078b62beca95..bb8d71fbae8711721c4183dd30ca4be1053bb532 100644
--- a/app/assets/stylesheets/framework/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
@@ -45,6 +45,7 @@
   &.s32 { font-size: 20px; line-height: 32px; }
   &.s40 { font-size: 16px; line-height: 40px; }
   &.s60 { font-size: 32px; line-height: 60px; }
+  &.s70 { font-size: 34px; line-height: 70px; }
   &.s90 { font-size: 36px; line-height: 90px; }
   &.s110 { font-size: 40px; line-height: 112px; font-weight: 300; }
   &.s140 { font-size: 72px; line-height: 140px; }
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 434a26d57c66723c5991c3c918a73f0016b8fc34..fab96404a6c44c0a02acc3b6845e1e6a13d8faf4 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -24,8 +24,8 @@
   background-color: $background-color;
   padding: $gl-padding;
   margin-bottom: 0;
-  border-top: 1px solid $border-color;
-  border-bottom: 1px solid $border-color;
+  border-top: 1px solid $white-dark;
+  border-bottom: 1px solid $white-dark;
   color: $gl-gray;
 
   &.oneline-block {
@@ -61,6 +61,11 @@
     margin-bottom: -$gl-padding;
   }
 
+  &.content-component-block {
+    padding: 11px 0;
+    background-color: $white-light;
+  }
+
   .title {
     color: $gl-text-color;
   }
@@ -110,9 +115,9 @@
   .cover-title {
     color: $gl-header-color;
     margin: 0;
-    font-size: 23px;
+    font-size: 24px;
     font-weight: normal;
-    margin: 16px 0 5px;
+    margin-bottom: 5px;
     color: #4c4e54;
     font-size: 23px;
     line-height: 1.1;
@@ -137,7 +142,6 @@
   }
 
   .cover-desc {
-    padding: 0 $gl-padding 3px;
     color: $gl-text-color;
 
     &.username:last-child {
@@ -205,7 +209,7 @@
 
 .content-block {
   padding: $gl-padding 0;
-  border-bottom: 1px solid $border-color;
+  border-bottom: 1px solid $white-dark;
 
   &.oneline-block {
     line-height: 36px;
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index eaf85bb17ca4876c191e853905d8e9756bf6c44e..1e3083cce55a5a0a9019767f96785fa66a656ff0 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -16,6 +16,19 @@
   @include btn-default;
 }
 
+@mixin btn-outline($background, $text, $border, $hover-background, $hover-text, $hover-border) {
+  background-color: $background;
+  color: $text;
+  border-color: $border;
+
+  &:hover,
+  &:focus {
+    background-color: $hover-background;
+    color: $hover-text;
+    border-color: $hover-border;;
+  }
+}
+
 @mixin btn-color($light, $border-light, $normal, $border-normal, $dark, $border-dark, $color) {
   background-color: $light;
   border-color: $border-light;
@@ -66,6 +79,23 @@
   @include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-white-dark, $btn-white-active);
 }
 
+@mixin btn-with-margin {
+  margin-left: $btn-side-margin;
+  float: left;
+
+  &.inline {
+    float: none;
+  }
+
+  &.btn-sm {
+    margin-left: $btn-sm-side-margin;
+  }
+
+  &.btn-xs {
+    margin-left: $btn-xs-side-margin;
+  }
+}
+
 .btn {
   @include btn-default;
   @include btn-white;
@@ -106,11 +136,14 @@
     @include btn-blue;
   }
 
-  &.btn-close,
   &.btn-warning {
     @include btn-orange;
   }
 
+  &.btn-close {
+    @include btn-outline($white-light, $orange-normal, $orange-normal, $orange-light, $white-light, $orange-light);
+  }
+
   &.btn-danger,
   &.btn-remove,
   &.btn-red {
@@ -126,15 +159,9 @@
   }
 
   &.btn-grouped {
-    margin-right: 7px;
-    float: left;
-    &:last-child {
-      margin-right: 0;
-    }
-    &.btn-xs {
-      margin-right: 3px;
-    }
+    @include btn-with-margin;
   }
+
   &.disabled {
     pointer-events: auto !important;
   }
@@ -176,11 +203,7 @@
 
 .btn-group {
   &.btn-grouped {
-    margin-right: 7px;
-    float: left;
-    &:last-child {
-      margin-right: 0;
-    }
+    @include btn-with-margin;
   }
 }
 
diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss
index 11f39d583bd1755fd27268806a4012d70f0c6b50..8642b7530e25ca386058cb2a585700a092394d5d 100644
--- a/app/assets/stylesheets/framework/calendar.scss
+++ b/app/assets/stylesheets/framework/calendar.scss
@@ -1,70 +1,44 @@
 .calender-block {
+  padding-left: 0;
+  padding-right: 0;
+
   @media (min-width: $screen-sm-min) and (max-width: $screen-lg-min) {
     overflow-x: scroll;
   }
 }
 
 .user-calendar-activities {
-  .calendar_onclick_hr {
-    padding: 0;
-    margin: 10px 0;
-  }
-
   .str-truncated {
     max-width: 70%;
   }
 
-  .text-expander {
-    background: #eee;
-    color: #555;
-    padding: 0 5px;
-    cursor: pointer;
-    margin-left: 4px;
-    &:hover {
-      background-color: #ddd;
-    }
+  .user-calendar-activities-loading {
+    font-size: 24px;
   }
 }
 
-/**
-* This overwrites the default values of the cal-heatmap gem
-*/
-.calendar {
-  .qi {
-    fill: #fff;
-  }
-
-  .q1 {
-    fill: #ededed !important;
-  }
+.user-calendar {
+  text-align: center;
 
-  .q2 {
-    fill: #acd5f2 !important;
-  }
-
-  .q3 {
-    fill: #7fa8d1 !important;
-  }
-
-  .q4 {
-    fill: #49729b !important;
-  }
-
-  .q5 {
-    fill: #254e77 !important;
+  .calendar {
+    display: inline-block;
   }
+}
 
-  .future {
-    visibility: hidden;
+.user-contrib-cell {
+  &:hover {
+    cursor: pointer;
+    stroke: #000;
   }
+}
 
-  .domain-background {
-    fill: none;
-    shape-rendering: crispedges;
-  }
+.user-contrib-text {
+  font-size: 12px;
+  fill: #959494;
+}
 
-  .ch-tooltip {
-    padding: 3px;
-    font-weight: 550;
-  }
+.calendar-hint {
+  margin-top: -23px;
+  float: right;
+  font-size: 12px;
 }
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 4bf3a0504030f12e68a49f47bcf7391efb10898e..d4d579a083d65b17b2a3bf6b5f404f6511e1fe8a 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -122,10 +122,9 @@
   a {
     display: block;
     position: relative;
-    padding-left: 10px;
-    padding-right: 10px;
+    padding: 5px 10px;
     color: $dropdown-link-color;
-    line-height: 34px;
+    line-height: initial;
     text-overflow: ellipsis;
     border-radius: 2px;
     white-space: nowrap;
@@ -154,7 +153,7 @@
     color: $dropdown-header-color;
     font-size: 13px;
     line-height: 22px;
-    padding: 0 10px 10px;
+    padding: 0 10px;
   }
 
   .separator + .dropdown-header {
@@ -162,6 +161,20 @@
   }
 }
 
+.dropdown-menu-large {
+  width: 340px;
+}
+
+.dropdown-menu-no-wrap {
+  a {
+    white-space: normal;
+  }
+}
+
+.dropdown-menu-full-width {
+  width: 100%;
+}
+
 .dropdown-menu-paging {
   .dropdown-page-two,
   .dropdown-menu-back {
@@ -228,13 +241,11 @@
   a {
     padding-left: 25px;
 
-    &.is-active {
+    &.is-indeterminate, &.is-active {
       &::before {
-        content: "\f00c";
         position: absolute;
         left: 5px;
-        top: 50%;
-        margin-top: -7px;
+        top: 8px;
         font: normal normal normal 14px/1 FontAwesome;
         font-size: inherit;
         text-rendering: auto;
@@ -242,6 +253,14 @@
         -moz-osx-font-smoothing: grayscale;
       }
     }
+
+    &.is-indeterminate::before {
+      content: "\f068";
+    }
+
+    &.is-active::before {
+      content: "\f00c";
+    }
   }
 }
 
@@ -521,3 +540,14 @@
     background-color: $calendar-unselectable-bg;
   }
 }
+
+.dropdown-menu-inner-title {
+  display: block;
+  color: $gl-title-color;
+  font-weight: 600;
+}
+
+.dropdown-menu-inner-content {
+  display: block;
+  color: $gl-placeholder-color;
+}
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index 8c96c7a9c31e8c554545a9a1c0776ceacb4ad3bc..71a9f79be3ef5e5c19e21382db30eb00f6267d17 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -5,6 +5,10 @@
 .file-holder {
   border: 1px solid $border-color;
 
+  &.file-holder-no-border {
+    border: 0;
+  }
+
   &.readme-holder {
     margin: $gl-padding-top 0;
   }
@@ -23,8 +27,17 @@
     word-wrap: break-word;
     border-radius: 3px 3px 0 0;
 
+    &.file-title-clear {
+      padding-left: 0;
+      padding-right: 0;
+      background-color: transparent;
+
+      .file-actions {
+        right: 0;
+      }
+    }
+
     .file-actions {
-      float: right;
       position: absolute;
       top: 5px;
       right: 15px;
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index 558b133f593ff94a466c04db0b2bc855a8c08bb1..43d5566154147f7323dc91353c7e9c7abc8bd25f 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -28,10 +28,6 @@ input[type='text'].danger {
 }
 
 label {
-  &.control-label {
-    @extend .col-sm-2;
-  }
-
   &.inline-label {
     margin: 0;
   }
@@ -41,6 +37,10 @@ label {
   }
 }
 
+.control-label {
+  @extend .col-sm-2;
+}
+
 .inline-input-group {
   width: 250px;
 }
@@ -76,6 +76,7 @@ label {
 .form-control {
   @include box-shadow(none);
   border-radius: 3px;
+  padding: $gl-vert-padding $gl-input-padding;
 }
 
 .select-wrapper {
diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss
index 16cf394c426eabce9fd65babb9f88ab6ecaed48f..0a8603b6702006358f6cafdfe562bb1a30b7287a 100644
--- a/app/assets/stylesheets/framework/gitlab-theme.scss
+++ b/app/assets/stylesheets/framework/gitlab-theme.scss
@@ -8,34 +8,16 @@
  */
 @mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) {
   .page-with-sidebar {
-    .header-logo {
-      background: $color-darker;
-
-      a {
-        color: $color-light;
-
-        h3 {
-          color: $color-light;
-        }
-      }
+    .toggle-nav-collapse,
+    .pin-nav-btn {
+      color: $color-light;
+      background: $color;
 
       &:hover {
-        background-color: $color-dark;
-        a {
-          color: #fff;
-
-          h3 {
-            color: #fff;
-          }
-        }
+        color: $white-light;
       }
     }
 
-    .collapse-nav a {
-      color: #fff;
-      background: $color;
-    }
-
     .sidebar-wrapper {
       background: $color-darker;
 
@@ -45,7 +27,7 @@
 
         &:hover {
           background-color: $color-dark;
-          color: #fff;
+          color: $white-light;
           text-decoration: none;
         }
       }
@@ -63,10 +45,20 @@
           color: $color-light;
         }
 
+        path,
+        polygon {
+          fill: $color-light;
+        }
+
         .count {
           color: $color-light;
           background: $color-dark;
         }
+
+        svg {
+          position: relative;
+          top: 3px;
+        }
       }
 
       &.separate-item {
@@ -74,7 +66,7 @@
       }
 
       &.active a {
-        color: #fff;
+        color: $white-light;
         background: $color-dark;
 
         &.no-highlight {
@@ -82,15 +74,23 @@
         }
 
         i {
-          color: #fff
+          color: $white-light
+        }
+
+        path,
+        polygon {
+          fill: $white-light;
         }
       }
     }
   }
 }
 
-$theme-blue: #2980b9;
 $theme-charcoal: #3d454d;
+$theme-charcoal-dark: #383f45;
+$theme-charcoal-text: #b9bbbe;
+
+$theme-blue: #2980b9;
 $theme-graphite: #666;
 $theme-gray: #373737;
 $theme-green: #019875;
@@ -102,7 +102,7 @@ body {
   }
 
   &.ui_charcoal {
-    @include gitlab-theme(#d6d7d9, #485157, $theme-charcoal, #353b41);
+    @include gitlab-theme($theme-charcoal-text, #485157, $theme-charcoal, $theme-charcoal-dark);
   }
 
   &.ui_graphite {
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 0da96c4017dbf6d47d56ed3555373c618dad4021..dca4dbb9f7d74e806903b7d9006418da060db5ca 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -3,7 +3,7 @@
  *
  */
 header {
-  transition-duration: .3s;
+  transition: padding $sidebar-transition-duration;
 
   &.navbar-empty {
     height: $header-height;
@@ -82,10 +82,10 @@ header {
     }
 
     .side-nav-toggle {
-      display: none;
       position: absolute;
       left: -10px;
       margin: 6px 0;
+      font-size: 18px;
       padding: 6px 10px;
       border: none;
       background-color: $background-color;
@@ -97,21 +97,13 @@ header {
       &:focus {
         outline: none;
       }
-
-      @media (max-width: $screen-xs-min) {
-        display: block;
-      }
     }
   }
 
   .header-content {
     position: relative;
     height: $header-height;
-    padding-right: 40px;
-
-    @media (max-width: $screen-xs-min) {
-      padding-left: 40px;
-    }
+    padding-left: 30px;
 
     @media (min-width: $screen-sm-min) {
       padding-right: 0;
@@ -121,9 +113,29 @@ header {
       margin-top: -5px;
     }
 
+    .header-logo {
+      position: absolute;
+      left: 50%;
+      margin-left: -18px;
+      top: 7px;
+      transition-duration: .3s;
+      z-index: 999;
+
+      &:hover {
+        cursor: pointer;
+      }
+
+      @media (max-width: $screen-xs-max) {
+        right: 25px;
+        left: auto;
+      }
+    }
+
     .title {
       margin: 0;
       font-size: 19px;
+      max-width: 400px;
+      display: inline-block;
       line-height: $header-height;
       font-weight: normal;
       color: $gl-text-color;
@@ -132,6 +144,10 @@ header {
       vertical-align: top;
       white-space: nowrap;
 
+      @media (max-width: $screen-sm-max) {
+        max-width: 190px;
+      }
+
       a {
         color: $gl-text-color;
         &:hover {
@@ -159,6 +175,10 @@ header {
     .navbar-collapse {
       float: right;
       border-top: none;
+
+      @media (max-width: $screen-xs-max) {
+        float: none;
+      }
     }
   }
 
@@ -171,31 +191,12 @@ header {
   }
 }
 
-@mixin collapsed-header {
-  margin-left: $sidebar_collapsed_width;
-}
-
-.header-collapsed {
-  margin-left: $sidebar_collapsed_width;
-
-  @media (min-width: $screen-md-min) {
-    @include collapsed-header;
-  }
-
-  @media (max-width: $screen-xs-min) {
-    margin-left: 0;
-  }
-}
-
-.header-expanded {
-  margin-left: $sidebar_collapsed_width;
-
-  @media (min-width: $screen-md-min) {
-    margin-left: $sidebar_width;
-  }
+.tanuki-shape {
+  transition: all 0.8s;
 
-  @media (max-width: $screen-xs-min) {
-    margin-left: 0;
+  &:hover, &.highlight {
+    fill: rgb(255, 255, 255);
+    transition: all 0.1s;
   }
 }
 
diff --git a/app/assets/stylesheets/framework/jquery.scss b/app/assets/stylesheets/framework/jquery.scss
index 525ed81b0591413b5f53f4dd09d23527d08bf965..30a5b837d696b69ebefe7988a5d0e9f7b5709503 100644
--- a/app/assets/stylesheets/framework/jquery.scss
+++ b/app/assets/stylesheets/framework/jquery.scss
@@ -2,6 +2,7 @@
   font-family: $regular_font;
   font-size: $font-size-base;
 
+  &.ui-datepicker,
   &.ui-datepicker-inline {
     border: 1px solid #ddd;
     padding: 10px;
@@ -10,6 +11,25 @@
     .ui-datepicker-header {
       background: #fff;
       border-color: #ddd;
+
+      .ui-datepicker-prev,
+      .ui-datepicker-next {
+        top: 4px;
+      }
+
+      .ui-datepicker-prev {
+        left: 2px;
+      }
+
+      .ui-datepicker-next {
+        right: 2px;
+      }
+
+      .ui-state-hover {
+        background: transparent;
+        border: 0;
+        cursor: pointer;
+      }
     }
 
     .ui-datepicker-calendar td a {
@@ -36,21 +56,18 @@
   }
 
   .ui-state-highlight {
-    border: 1px solid #eee;
-    background: #eee;
+    border: 0;
+    background: transparent;
   }
 
-  .ui-state-active {
-    border: 1px solid $gl-primary;
-    background: $gl-primary;
-    color: #fff;
-  }
-
-  .ui-state-hover,
-  .ui-state-focus {
-    border: 1px solid $row-hover;
-    background: $row-hover;
-    color: #333;
+  .ui-datepicker-calendar {
+    .ui-state-active,
+    .ui-state-hover,
+    .ui-state-focus {
+      border: 1px solid $gl-primary;
+      background: $gl-primary;
+      color: #fff;
+    }
   }
 }
 
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index b17c8bcbb1eb3e33a38067cdbbb36cf2d47d3093..a12c0bba44a24ade1fa0e114fd5ec02ffe5ae3db 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -137,10 +137,30 @@ ul.content-list {
       padding-top: 1px;
       float: right;
 
-      .btn {
-        padding: 10px 14px;
+      > .btn,
+      > .btn-group {
+        margin-right: $gl-padding-top;
+        display: inline-block;
+        margin-top: 4px;
+        margin-bottom: 4px;
+
+        &:last-child {
+          margin-right: 0;
+        }
       }
     }
+
+    // When dragging a list item
+    &.ui-sortable-helper {
+      border-bottom: none;
+    }
+
+    &.list-placeholder {
+      background-color: $gray-light;
+      border: dotted 1px $gray-dark;
+      margin: 1px 0;
+      min-height: 52px;
+    }
   }
 }
 
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index 250d630929176d2a9f35b4c545561bc3d66121f1..828e72242319425fcb79d2d20824bedbacc37d26 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -2,18 +2,10 @@
  * Generic mixins
  */
 @mixin box-shadow($shadow) {
-  -webkit-box-shadow: $shadow;
-  -moz-box-shadow: $shadow;
-  -ms-box-shadow: $shadow;
-  -o-box-shadow: $shadow;
   box-shadow: $shadow;
 }
 
 @mixin border-radius($radius) {
-  -webkit-border-radius: $radius;
-  -moz-border-radius: $radius;
-  -ms-border-radius: $radius;
-  -o-border-radius: $radius;
   border-radius: $radius;
 }
 
diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss
index 33cbee85987ae7220ce0470e55d57a14d4efc089..d4e5cc819a459d9f4d56c6ea4dface37a04537f0 100644
--- a/app/assets/stylesheets/framework/mobile.scss
+++ b/app/assets/stylesheets/framework/mobile.scss
@@ -48,10 +48,6 @@
       display: block;
     }
 
-    .project-home-desc {
-      font-size: 21px;
-    }
-
     .project-repo-buttons,
     .git-clone-holder {
       display: none;
@@ -70,10 +66,6 @@
     display: none;
   }
 
-  %ul.notes .note-role, .note-actions {
-    display: none;
-  }
-
   .nav-links, .nav-links {
     li a {
       font-size: 14px;
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index a81fcb1c6b396b07296f12c9af8faebb26b46c02..a55918f87113544be641c98e9c79e17b5fb0a759 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -1,3 +1,35 @@
+@mixin fade($gradient-direction, $rgba, $gradient-color) {
+  visibility: visible;
+  opacity: 1;
+  z-index: 2;
+  position: absolute;
+  bottom: 12px;
+  width: 43px;
+  height: 30px;
+  transition-duration: .3s;
+  -webkit-transform: translateZ(0);
+  background: -webkit-linear-gradient($gradient-direction, $rgba, $gradient-color 45%);
+  background: -o-linear-gradient($gradient-direction, $rgba, $gradient-color 45%);
+  background: -moz-linear-gradient($gradient-direction, $rgba, $gradient-color 45%);
+  background: linear-gradient($gradient-direction, $rgba, $gradient-color 45%);
+
+  &.end-scroll {
+    visibility: hidden;
+    opacity: 0;
+    transition-duration: .3s;
+  }
+}
+
+@mixin scrolling-links() {
+  white-space: nowrap;
+  overflow-x: auto;
+  overflow-y: hidden;
+  -webkit-overflow-scrolling: touch;
+  &::-webkit-scrollbar {
+    display: none;
+  }
+}
+
 .nav-links {
   padding: 0;
   margin: 0;
@@ -10,8 +42,7 @@
 
     a {
       display: inline-block;
-      padding: 14px;
-      padding-top: $gl-padding;
+      padding: $gl-btn-padding;
       padding-bottom: 11px;
       margin-bottom: -1px;
       font-size: 15px;
@@ -36,6 +67,29 @@
       color: #78a;
     }
   }
+
+  &.sub-nav {
+    text-align: center;
+    background-color: $background-color;
+
+    .container-fluid {
+      background-color: $background-color;
+      margin-bottom: 0;
+    }
+
+    li {
+
+      a {
+        margin: 0;
+        padding: 11px 10px 9px;
+      }
+
+      &.active a {
+        border-bottom: none;
+        color: $link-underline-blue;
+      }
+    }
+  }
 }
 
 .top-area {
@@ -50,6 +104,10 @@
     width: 50%;
     line-height: 28px;
 
+    &.wiki-page {
+      padding: 16px 10px 11px;
+    }
+
     /* Small devices (phones, tablets, 768px and lower) */
     @media (max-width: $screen-sm-min) {
       width: 100%;
@@ -73,6 +131,10 @@
     margin-bottom: 0;
     border-bottom: none;
 
+    li a {
+      padding: 16px 10px 11px;
+    }
+
     /* Small devices (phones, tablets, 768px and lower) */
     @media (max-width: $screen-sm-max) {
       width: 100%;
@@ -119,7 +181,7 @@
     }
 
     input {
-      height: 34px;
+      height: 35px;
       display: inline-block;
       position: relative;
       top: 2px;
@@ -148,7 +210,7 @@
 
     @media (max-width: $screen-xs-max) {
       padding-bottom: 0;
-
+      width: 100%;
       .btn, form, .dropdown, .dropdown-menu-toggle, .form-control {
         margin: 0 0 10px;
         display: block;
@@ -179,15 +241,11 @@
         margin: 0;
       }
     }
+  }
 
-    /* Small devices (tablets, 768px and lower) */
-    @media (max-width: $screen-sm-max) {
-      width: 100%;
-      text-align: left;
-
-      input {
-        width: 300px;
-      }
+  &.adjust {
+    .nav-text, .nav-controls {
+      width: auto;
     }
   }
 }
@@ -196,10 +254,11 @@
   position: fixed;
   top: $header-height;
   width: 100%;
-  z-index: 1;
+  z-index: 11;
   background: $background-color;
   border-bottom: 1px solid $border-color;
-  transition-duration: .3s;
+  transition: padding $sidebar-transition-duration;
+  text-align: center;
 
   .container-fluid {
     position: relative;
@@ -209,13 +268,8 @@
     float: right;
     padding: 7px 0 0;
 
-    @media (max-width: $screen-xs-min) {
-      float: none;
-      padding: 0 9px;
-
-      .dropdown-new {
-        width: 100%;
-      }
+    @media (max-width: $screen-xs-max) {
+      display: none;
     }
 
     i {
@@ -233,19 +287,44 @@
     }
 
     .dropdown {
-      margin-left: 7px;
+      position: absolute;
+      top: 7px;
+      right: 15px;
+      z-index: 2;
 
-      @media (max-width: $screen-xs-min) {
-        margin-left: 0;
+      li.active {
+        font-weight: bold;
       }
     }
   }
 
   .nav-links {
+    @include scrolling-links();
     border-bottom: none;
     height: 51px;
-    white-space: nowrap;
-    overflow-x: auto;
+
+    svg {
+      position: relative;
+      top: 2px;
+      margin-right: 2px;
+      height: 15px;
+      width: auto;
+
+      path,
+      polygon {
+        fill: $layout-link-gray;
+      }
+    }
+
+    .fade-right {
+      @include fade(left, rgba(250, 250, 250, 0.4), $background-color);
+      right: 0;
+    }
+
+    .fade-left {
+      @include fade(right, rgba(250, 250, 250, 0.4), $background-color);
+      left: 0;
+    }
 
     li {
 
@@ -258,25 +337,105 @@
       }
 
       &.active {
+
         a, i {
           color: $black;
         }
+
+        svg {
+          path,
+          polygon {
+            fill: $black;
+          }
+        }
       }
 
       .badge {
         color: $gl-icon-color;
       }
+
+      &:hover {
+        a, i {
+          color: $black;
+        }
+      }
+    }
+  }
+
+  .nav-control {
+
+    .fade-right {
+      @media (min-width: $screen-xs-max) {
+        right: 68px;
+      }
+      @media (max-width: $screen-xs-min) {
+        right: 0;
+      }
+    }
+  }
+}
+
+.scrolling-tabs-container {
+  position: relative;
+
+  .nav-links {
+    @include scrolling-links();
+
+    .fade-right {
+      @include fade(left, rgba(255, 255, 255, 0.4), $background-color);
+      right: 0;
+    }
+
+    .fade-left {
+      @include fade(right, rgba(255, 255, 255, 0.4), $background-color);
+      left: 0;
     }
   }
+}
 
+.nav-block {
+  position: relative;
+
+  .nav-links {
+    @include scrolling-links();
+
+    .fade-right {
+      @include fade(left, rgba(255, 255, 255, 0.4), $white-light);
+      right: 0;
+    }
+
+    .fade-left {
+      @include fade(right, rgba(255, 255, 255, 0.4), $white-light);
+      left: 0;
+    }
+
+    &.event-filter {
+      .fade-right {
+        visibility: hidden;
+
+        @media (max-width: $screen-xs-max) {
+          visibility: visible;
+        }
+      }
+    }
+  }
 }
 
 .page-with-layout-nav {
-  margin-top: 50px;
+  margin-top: $header-height + 2;
+
+  .right-sidebar {
+    top: ($header-height * 2) + 2;
+  }
+}
+
+.activities {
+
+  .nav-block {
+    border-bottom: 1px solid $border-color;
 
-  &.controls-dropdown-visible {
-    @media (max-width: $screen-xs-min) {
-      margin-top: 96px;
+    .nav-links {
+      border-bottom: none;
     }
   }
 }
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index 54196e8894d688b3066f85de99157849dd88931e..37a1f85da7705fd31da3e8d5c311c08b98c1868d 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -8,7 +8,7 @@
     background: #fff;
     border-color: $input-border;
     height: 35px;
-    padding: $gl-vert-padding $gl-btn-padding;
+    padding: $gl-vert-padding $gl-input-padding;
     font-size: $gl-font-size;
     line-height: 1.42857143;
     border-radius: $border-radius-base;
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index f90d7a806d3d8db3f2dde9383999ee34691ed60d..281c0a0e1e927415d9564ed220a1304b15722255 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -1,40 +1,31 @@
-#logo {
-  z-index: 2;
-  position: absolute;
-  width: 58px;
-  cursor: pointer;
-  margin-top: 8px;
-}
-
 .page-with-sidebar {
   padding-top: $header-height;
-  transition-duration: .3s;
+  transition: padding $sidebar-transition-duration;
 
   .sidebar-wrapper {
     position: fixed;
     top: 0;
     bottom: 0;
-    overflow-y: auto;
-    overflow-x: hidden;
     left: 0;
     height: 100%;
-    transition-duration: .3s;
-  }
-
-  .gitlab-text-container-link {
-    z-index: 1;
-    position: absolute;
-    left: 0;
+    overflow: hidden;
+    transition: width $sidebar-transition-duration;
   }
 }
 
 .sidebar-wrapper {
   z-index: 1000;
   background: $background-color;
+
+  .nicescroll-rails-hr {
+    // TODO: Figure out why nicescroll doesn't hide horizontal bar
+    display: none!important;
+  }
 }
 
 .content-wrapper {
   width: 100%;
+  transition: padding $sidebar-transition-duration;
 
   .container-fluid {
     background: #fff;
@@ -48,69 +39,19 @@
   }
 }
 
-.sidebar-wrapper {
-  .header-logo {
-    border-bottom: 1px solid transparent;
-    float: left;
-    height: $header-height;
-    width: $sidebar_width;
-    position: fixed;
-    z-index: 999;
-    overflow: hidden;
-    transition-duration: .3s;
-
-    a {
-      float: left;
-      height: $header-height;
-      width: 100%;
-      padding-left: 22px;
-      overflow: hidden;
-      outline: none;
-      transition-duration: .3s;
-
-      img {
-        width: 36px;
-        height: 36px;
-      }
-
-      #tanuki-logo, img {
-        float: left;
-      }
-
-      .gitlab-text-container {
-        width: 230px;
-
-        h3 {
-          width: 158px;
-          float: left;
-          margin: 0;
-          margin-left: 50px;
-          font-size: 19px;
-          line-height: 50px;
-          font-weight: normal;
-        }
-      }
-    }
-
-    &:hover {
-      background-color: #eee;
-    }
-  }
-
-  .sidebar-user {
-    padding: 7px 22px;
-    position: fixed;
-    bottom: 40px;
-    width: $sidebar_width;
-    overflow: hidden;
-    transition-duration: .3s;
+.sidebar-user {
+  padding: 15px;
+  position: absolute;
+  left: 0;
+  bottom: 0;
+  width: $sidebar_width;
+  overflow: hidden;
+  font-size: 16px;
+  line-height: 36px;
+  transition: width $sidebar-transition-duration, padding $sidebar-transition-duration;
 
-    .username {
-      margin-left: 10px;
-      width: $sidebar_width - 2 * 10px;
-      font-size: 16px;
-      line-height: 34px;
-    }
+  @media (min-width: $sidebar-breakpoint) {
+    bottom: 50px;
   }
 }
 
@@ -126,213 +67,147 @@
 
 
 .nav-sidebar {
-  margin-top: 14 + $header-height;
-  margin-bottom: 100px;
-  transition-duration: .3s;
-  list-style: none;
-  overflow: hidden;
+  position: absolute;
+  top: 50px;
+  bottom: 65px;
+  width: $sidebar_width;
+  overflow-y: auto;
+  overflow-x: hidden;
+
+  @media (min-width: $sidebar-breakpoint) {
+    bottom: 115px;
+  }
 
   &.navbar-collapse {
     padding: 0 !important;
   }
 
   li {
-    width: $sidebar_width;
-
     &.separate-item {
       padding-top: 10px;
       margin-top: 10px;
     }
 
+    .icon-container {
+      width: 34px;
+      display: inline-block;
+      text-align: center;
+    }
+
     a {
-      padding: 7px 15px;
+      padding: 7px 15px 7px 12px;
       font-size: $gl-font-size;
       line-height: 24px;
-      color: $gray;
       display: block;
       text-decoration: none;
-      padding-left: 23px;
       font-weight: normal;
       outline: none;
+      white-space: nowrap;
 
-      &:hover {
-        text-decoration: none;
-      }
-
-      &:active, &:focus {
+      &:hover,
+      &:active,
+      &:focus {
         text-decoration: none;
       }
 
       i {
-        width: 16px;
-        color: $gray-light;
-        margin-right: 13px;
-      }
-
-      .count {
-        float: right;
-        background: #eee;
-        padding: 0 8px;
-        @include border-radius(6px);
+        font-size: 16px;
       }
 
-      &.back-link i {
-        transition-duration: .3s;
+      i,
+      svg {
+        margin-right: 13px;
       }
     }
   }
-}
-
-.sidebar-subnav {
-  margin-left: 0;
-  padding-left: 0;
 
-  li {
-    list-style: none;
+  .count {
+    float: right;
+    padding: 0 8px;
+    @include border-radius(6px);
   }
 }
 
-.collapse-nav a {
+.toggle-nav-collapse {
   width: $sidebar_width;
-  position: fixed;
-  bottom: 0;
+  position: absolute;
+  top: 0;
   left: 0;
-  font-size: 13px;
-  background: transparent;
-  height: 40px;
-  text-align: center;
-  line-height: 40px;
+  min-height: 50px;
+  padding: 5px 0;
+  font-size: 18px;
+  line-height: 30px;
+}
+
+.nav-header-btn {
+  padding: 10px 5px;
+  color: inherit;
   transition-duration: .3s;
-  outline: none;
 
-  &:hover {
+  &:hover,
+  &:focus {
+    color: $white-light;
     text-decoration: none;
   }
 }
 
-.sidebar-wrapper {
-  &.hidden-nav {
-    width: 0;
-  }
-}
-
-.page-sidebar-collapsed {
-  padding-left: $sidebar_collapsed_width;
+.pin-nav-btn {
+  display: none;
+  position: absolute;
+  left: 0;
+  bottom: 0;
+  height: 50px;
+  width: $sidebar_width;
+  line-height: 30px;
 
-  @media (max-width: $screen-xs-min) {
-    padding-left: 0;
+  @media (min-width: $sidebar-breakpoint) {
+    display: block;
   }
 
-  .sidebar-wrapper {
-    width: $sidebar_collapsed_width;
-
-    @media (max-width: $screen-xs-min) {
-      width: 0;
-    }
-
-    .header-logo {
-      width: $sidebar_collapsed_width;
-
-      @media (max-width: $screen-xs-min) {
-        width: 0;
-      }
-
-      a {
-        padding-left: ($sidebar_collapsed_width - 36) / 2;
-
-        .gitlab-text-container {
-          display: none;
-        }
-      }
-    }
-
-    .nav-sidebar {
-      width: $sidebar_collapsed_width;
-
-      li {
-        width: auto;
-
-        a {
-          span {
-            display: none;
-          }
-        }
-      }
-    }
-
-    .collapse-nav a {
-      width: $sidebar_collapsed_width;
-
-      @media (max-width: $screen-xs-min) {
-        width: 0;
-      }
-    }
-
-    .sidebar-user {
-      padding-left: ($sidebar_collapsed_width - 36) / 2;
-      width: $sidebar_collapsed_width;
-
-      @media (max-width: $screen-xs-min) {
-        width: 0;
-        padding-left: 0;
-        padding-right: 0;
-      }
-
-      .username {
-        display: none;
-      }
-    }
+  .fa {
+    transition: transform .15s;
   }
 
-  .layout-nav {
-    padding-right: $sidebar_collapsed_width;
-
-    @media (max-width: $screen-xs-min) {
-      padding-right: 0;;
+  &.is-active {
+    .fa {
+      transform: rotate(90deg);
     }
   }
 }
 
-.page-sidebar-expanded {
-  padding-left: $sidebar_collapsed_width;
-
-  @media (min-width: $screen-md-min) {
-    padding-left: $sidebar_width;
-  }
+.page-sidebar-collapsed {
+  padding-left: 0;
 
-  @media (max-width: $screen-xs-min) {
-    padding-left: 0;
+  .sidebar-wrapper {
+    width: 0;
   }
+}
 
+.page-sidebar-expanded {
   .sidebar-wrapper {
     width: $sidebar_width;
-
-    .nav-sidebar {
-      width: $sidebar_width;
-    }
-
-    .nav-sidebar li a {
-      width: $sidebar_width;
-
-      &.back-link {
-        i {
-          opacity: 0;
-        }
-      }
-    }
   }
+}
 
+.page-sidebar-pinned {
+  .content-wrapper,
   .layout-nav {
-    @media (max-width: $screen-xs-min) {
-      padding-right: 0;;
+    @media (min-width: $sidebar-breakpoint) {
+      padding-left: $sidebar_width;
     }
+  }
+}
 
-    @media (min-width: $screen-xs-min) and (max-width: $screen-md-min) {
-      padding-right: 62px;
+header.header-pinned-nav {
+  @media (min-width: $sidebar-breakpoint) {
+    padding-left: ($sidebar_width + $gl-padding);
+
+    .side-nav-toggle {
+      display: none;
     }
 
-    @media (min-width: $screen-md-min) {
-      padding-right: $sidebar_width;
+    .header-content {
+      padding-left: 0;
     }
   }
 }
@@ -353,7 +228,9 @@
   padding-right: 0;
 
   @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
-    padding-right: $sidebar_collapsed_width;
+    &:not(.build-sidebar) {
+      padding-right: $sidebar_collapsed_width;
+    }
   }
 
   @media (min-width: $screen-md-min) {
diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index 29501069d2788630c989bb3d9d1c04cb2f574e22..0b0bd80c3269e554fd9d5f8b4bb31e6dfb4f6ce8 100644
--- a/app/assets/stylesheets/framework/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
@@ -5,7 +5,7 @@
   padding: 0;
 
   .timeline-entry {
-    padding: $gl-padding $gl-btn-padding;
+    padding: $gl-padding $gl-btn-padding 11px;
     border-color: $table-border-color;
     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 6a45c34ccbbeee78671b522ea66c7d1ae96ac6cc..e3154657c5416a4d37cbda04017decae406bc629 100644
--- a/app/assets/stylesheets/framework/tw_bootstrap.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap.scss
@@ -192,3 +192,8 @@
 .text-info:hover {
   color: $brand-info;
 }
+
+// Prevent datetimes on tooltips to break into two lines
+.local-timeago {
+  white-space: nowrap;
+}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index c5a4dbe372c2297751608e05bd2e883cc57ab8f1..acada1f16a040c4eaf91a7c3a389ced6452cca44 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -6,6 +6,8 @@ $sidebar_width: 220px;
 $gutter_collapsed_width: 62px;
 $gutter_width: 290px;
 $gutter_inner_width: 258px;
+$sidebar-transition-duration: .15s;
+$sidebar-breakpoint: 1440px;
 
 /*
  * UI elements
@@ -57,13 +59,15 @@ $code_line_height: 1.5;
  */
 $gl-padding: 16px;
 $gl-btn-padding: 10px;
+$gl-input-padding: 10px;
 $gl-vert-padding: 6px;
 $gl-padding-top: 10px;
 
 /*
  * Misc
  */
-$row-hover: #f4f8fe;
+$row-hover: #f7faff;
+$row-hover-border: #b2d7ff;
 $progress-color: #c0392b;
 $avatar_radius: 50%;
 $header-height: 50px;
@@ -78,6 +82,9 @@ $provider-btn-not-active-color: #4688f1;
 $link-underline-blue: #4a8bee;
 $layout-link-gray: #7e7c7c;
 $todo-alert-blue: #428bca;
+$btn-side-margin: 10px;
+$btn-sm-side-margin: 7px;
+$btn-xs-side-margin: 5px;
 
 /*
  * Color schema
@@ -104,7 +111,7 @@ $blue-medium-light: #3498cb;
 $blue-medium: #2f8ebf;
 $blue-medium-dark: #2d86b4;
 
-$orange-light: rgba(252, 109, 38, 0.80);
+$orange-light: #fc8a51;
 $orange-normal: #e75e40;
 $orange-dark: #ce5237;
 
@@ -119,8 +126,8 @@ $border-white-light: #f1f2f4;
 $border-white-normal: #d6dae2;
 $border-white-dark: #c6cacf;
 
-$border-gray-light: rgba(0, 0, 0, 0.06);
-$border-gray-normal: rgba(0, 0, 0, 0.10);;
+$border-gray-light: #dcdcdc;
+$border-gray-normal: #d7d7d7;
 $border-gray-dark: #c6cacf;
 
 $border-green-light: #2faa60;
@@ -255,3 +262,6 @@ $calendar-header-color: #b8b8b8;
 $calendar-hover-bg: #ecf3fe;
 $calendar-border-color: rgba(#000, .1);
 $calendar-unselectable-bg: #faf9f9;
+
+$ci-output-bg: #1d1f21;
+$ci-text-color: #c5c8c6;
diff --git a/app/assets/stylesheets/mailers/devise.scss b/app/assets/stylesheets/mailers/devise.scss
new file mode 100644
index 0000000000000000000000000000000000000000..28611a5ec81f3378f7822de9db0cc26d3c954e2d
--- /dev/null
+++ b/app/assets/stylesheets/mailers/devise.scss
@@ -0,0 +1,134 @@
+// NOTE: This stylesheet is for the exclusive use of the `devise_mailer` layout
+// used for Devise email templates, and _should not_ be included in any
+// application stylesheets.
+//
+// Styles defined here are embedded directly into the resulting email HTML via
+// the `premailer` gem.
+
+$body-background-color:    #363636;
+$message-background-color: #fafafa;
+
+$header-color:             #6b4fbb;
+$body-color:               #444;
+$cta-color:                #e14329;
+$footer-link-color:        #7e7e7e;
+
+$font-family: Helvetica, Arial, sans-serif;
+
+body {
+  background-color: $body-background-color;
+  font-family: $font-family;
+  margin: 0;
+  padding: 0;
+}
+
+table {
+  -premailer-cellpadding: 0;
+  -premailer-cellspacing: 0;
+
+  border: 0;
+  border-collapse: separate;
+
+  &#wrapper {
+    background-color: $body-background-color;
+    width: 100%;
+  }
+
+  &#header {
+    margin: 0 auto;
+    text-align: left;
+    width: 600px;
+  }
+
+  &#body {
+    background-color: $message-background-color;
+    border: 1px solid #000;
+    border-radius: 4px;
+    margin: 0 auto;
+    width: 600px;
+  }
+
+  &#footer {
+    color: $footer-link-color;
+    font-size: 14px;
+    text-align: center;
+    width: 100%;
+  }
+
+  td {
+    &#body-container {
+      padding: 20px 40px;
+    }
+  }
+}
+
+.center {
+  text-align: center;
+}
+
+#logo {
+  border: none;
+  outline: none;
+  min-height: 88px;
+  width: 134px;
+}
+
+#content {
+  h2 {
+    color: $header-color;
+    font-size: 30px;
+    font-weight: 400;
+    line-height: 34px;
+    margin-top: 0;
+  }
+
+  p {
+    color: $body-color;
+    font-size: 17px;
+    line-height: 24px;
+    margin-bottom: 0;
+  }
+}
+
+#cta {
+  border: 1px solid $cta-color;
+  border-radius: 3px;
+  display: inline-block;
+  margin: 20px 0;
+  padding: 12px 24px;
+
+  a {
+    background-color: $message-background-color;
+    color: $cta-color;
+    display: inline-block;
+    text-decoration: none;
+  }
+}
+
+#tanuki {
+  padding: 40px 0 0;
+
+  img {
+    border: none;
+    outline: none;
+    width: 37px;
+    min-height: 36px;
+  }
+}
+
+#tagline {
+  font-size: 22px;
+  font-weight: 100;
+  padding: 4px 0 40px;
+}
+
+#social {
+  padding: 0 10px 20px;
+  width: 600px;
+  word-spacing: 20px;
+
+  a {
+    color: $footer-link-color;
+    text-decoration: none;
+  }
+}
diff --git a/app/assets/stylesheets/mailers/repository_push_email.scss b/app/assets/stylesheets/mailers/repository_push_email.scss
index 001994db97b72970e271e6f60f380883e2f33036..7f645d3089d0e22b13e8ac2430c180337fe26704 100644
--- a/app/assets/stylesheets/mailers/repository_push_email.scss
+++ b/app/assets/stylesheets/mailers/repository_push_email.scss
@@ -1,5 +1,15 @@
 @import "framework/variables";
 
+// This file is largely copied from `highlight/white.scss`, but modified to
+// avoid all descendant selectors (`table td`). This is because the CSS inlining
+// we use performs dramatically worse on descendant selectors than the
+// alternatives.
+// <https://gitlab.com/gitlab-org/gitlab-ee/issues/490#note_12283632>
+//
+// DO NOT ADD ANY DESCENDANT SELECTORS TO THIS FILE. Instead, use (in order of
+// preference): plain class selectors, type (element name) selectors, or
+// explicit child selectors.
+
 table.code {
   width: 100%;
   font-family: monospace;
@@ -11,33 +21,162 @@ table.code {
   -premailer-cellspacing: 0;
   -premailer-width: 100%;
 
-  td {
+  > tr > td {
     line-height: $code_line_height;
     font-family: monospace;
     font-size: $code_font_size;
+
+    &.diff-line-num {
+      margin: 0;
+      padding: 0;
+      border: none;
+      padding: 0 5px;
+      border-right: 1px solid;
+      text-align: right;
+      min-width: 35px;
+      max-width: 50px;
+      width: 35px;
+    }
+
+    &.line_content {
+      display: block;
+      margin: 0;
+      padding: 0 0.5em;
+      border: none;
+      white-space: pre;
+    }
   }
+}
+
+.line-numbers, .diff-line-num {
+  background-color: $background-color;
+}
+
+.diff-line-num, .diff-line-num a {
+  color: $black-transparent;
+}
 
-  td.diff-line-num {
-    margin: 0;
-    padding: 0;
-    border: none;
-    background: $background-color;
-    color: rgba(0, 0, 0, 0.3);
-    padding: 0 5px;
-    border-right: 1px solid $border-color;
-    text-align: right;
-    min-width: 35px;
-    max-width: 50px;
-    width: 35px;
+pre.code, .diff-line-num {
+  border-color: $table-border-gray;
+}
+
+.code.white, pre.code, .line_content {
+  background-color: #fff;
+  color: #333;
+}
+
+.diff-line-num {
+  &.old {
+    background-color: $line-number-old;
+    border-color: $line-removed-dark;
   }
 
-  td.line_content {
-    display: block;
-    margin: 0;
-    padding: 0 0.5em;
-    border: none;
-    white-space: pre;
+  &.new {
+    background-color: $line-number-new;
+    border-color: $line-added-dark;
   }
+
+  &.hll:not(.empty-cell) {
+    background-color: $line-number-select;
+    border-color: $line-select-yellow-dark;
+  }
+}
+
+.line_content {
+  &.old {
+    background-color: $line-removed;
+
+    > .line > span.idiff, > .line > span > span.idiff {
+      background-color: $line-removed-dark;
+    }
+  }
+
+  &.new {
+    background-color: $line-added;
+
+    > .line > span.idiff, > .line > span > span.idiff {
+      background-color: $line-added-dark;
+    }
+  }
+
+  &.match {
+    color: $black-transparent;
+    background-color: $match-line;
+  }
+
+  &.hll:not(.empty-cell) {
+    background-color: $line-select-yellow;
+  }
+}
+
+pre > .hll {
+  background-color: #f8eec7 !important;
+}
+
+span.highlight_word {
+  background-color: #fafe3d !important;
 }
 
-@import "highlight/white";
+.hll { background-color: #f8f8f8 }
+.c { color: #998; font-style: italic; }
+.err { color: #a61717; background-color: #e3d2d2; }
+.k { font-weight: bold; }
+.o { font-weight: bold; }
+.cm { color: #998; font-style: italic; }
+.cp { color: #999; font-weight: bold; }
+.c1 { color: #998; font-style: italic; }
+.cs { color: #999; font-weight: bold; font-style: italic; }
+.gd { color: #000; background-color: #fdd; }
+.gd .x { color: #000; background-color: #faa; }
+.ge { font-style: italic; }
+.gr { color: #a00; }
+.gh { color: #999; }
+.gi { color: #000; background-color: #dfd; }
+.gi .x { color: #000; background-color: #afa; }
+.go { color: #888; }
+.gp { color: #555; }
+.gs { font-weight: bold; }
+.gu { color: #800080; font-weight: bold; }
+.gt { color: #a00; }
+.kc { font-weight: bold; }
+.kd { font-weight: bold; }
+.kn { font-weight: bold; }
+.kp { font-weight: bold; }
+.kr { font-weight: bold; }
+.kt { color: #458; font-weight: bold; }
+.m { color: #099; }
+.s { color: #d14; }
+.n { color: #333; }
+.na { color: teal; }
+.nb { color: #0086b3; }
+.nc { color: #458; font-weight: bold; }
+.no { color: teal; }
+.ni { color: purple; }
+.ne { color: #900; font-weight: bold; }
+.nf { color: #900; font-weight: bold; }
+.nn { color: #555; }
+.nt { color: navy; }
+.nv { color: teal; }
+.ow { font-weight: bold; }
+.w { color: #bbb; }
+.mf { color: #099; }
+.mh { color: #099; }
+.mi { color: #099; }
+.mo { color: #099; }
+.sb { color: #d14; }
+.sc { color: #d14; }
+.sd { color: #d14; }
+.s2 { color: #d14; }
+.se { color: #d14; }
+.sh { color: #d14; }
+.si { color: #d14; }
+.sx { color: #d14; }
+.sr { color: #009926; }
+.s1 { color: #d14; }
+.ss { color: #990073; }
+.bp { color: #999; }
+.vc { color: teal; }
+.vg { color: teal; }
+.vi { color: teal; }
+.il { color: #099; }
+.gc { color: #999; background-color: #eaf2f5; }
diff --git a/app/assets/stylesheets/notify.scss b/app/assets/stylesheets/notify.scss
index 0a13a7e0b546fc0dbccfe304d9404f5f827618d8..fc12964872d2ebfca0f210dc37a6817f0a4e89cc 100644
--- a/app/assets/stylesheets/notify.scss
+++ b/app/assets/stylesheets/notify.scss
@@ -6,19 +6,19 @@ p.details {
   font-style: italic;
   color: #777
 }
-.footer p {
+.footer > p {
   font-size: small;
   color: #777
 }
 pre.commit-message {
   white-space: pre-wrap;
 }
-.file-stats a {
+.file-stats > a {
   text-decoration: none;
-}
-.file-stats .new-file {
-  color: #090;
-}
-.file-stats .deleted-file {
-  color: #b00;
+  > .new-file {
+    color: #090;
+  }
+  > .deleted-file {
+    color: #b00;
+  }
 }
diff --git a/app/assets/stylesheets/pages/awards.scss b/app/assets/stylesheets/pages/awards.scss
index 37bf38fa65db9d6d11ce4260f74e109d19d6a714..6211f3a52eb7f665b4d6103b83a08f5613f4526d 100644
--- a/app/assets/stylesheets/pages/awards.scss
+++ b/app/assets/stylesheets/pages/awards.scss
@@ -1,6 +1,4 @@
 .awards {
-  line-height: 34px;
-
   .emoji-icon {
     width: 20px;
     height: 20px;
@@ -9,8 +7,6 @@
 
 .emoji-menu {
   position: absolute;
-  top: 100%;
-  left: 0;
   margin-top: 3px;
   z-index: 1000;
   min-width: 160px;
@@ -23,7 +19,12 @@
   opacity: 0;
   transform: scale(.2);
   transform-origin: 0 -45px;
-  transition: all .3s cubic-bezier(.87,-.41,.19,1.44);
+  transition: .3s cubic-bezier(.87,-.41,.19,1.44);
+  transition-property: transform, opacity;
+
+  &.is-aligned-right {
+    transform-origin: 100% -45px;
+  }
 
   &.is-visible {
     pointer-events: all;
@@ -94,20 +95,30 @@
 
 .award-control {
   margin-right: 5px;
+  margin-bottom: 5px;
   padding-left: 5px;
   padding-right: 5px;
   line-height: 20px;
   outline: 0;
 
+  &:hover,
   &.active,
   &:active {
-    background-color: $white-dark;
+    background-color: $row-hover;
+    border-color: $row-hover-border;
     box-shadow: none;
     outline: 0;
   }
 
+  &.btn {
+    &:focus {
+      outline: 0;
+    }
+  }
+
   &.is-loading {
-    .award-control-icon {
+    .award-control-icon-normal,
+    .emoji-icon {
       display: none;
     }
 
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index aa41565f8128b9cb648aadee4c17bfb8bf09ccf9..e8f1935d239e95a813e57e34343b5ceb77c2012f 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -3,12 +3,7 @@
     background: #111;
     color: #fff;
     font-family: $monospace_font;
-    white-space: pre;
-    white-space: pre-wrap;       /* css-3 */
-    white-space: -moz-pre-wrap;  /* Mozilla, since 1999 */
-    white-space: -pre-wrap;      /* Opera 4-6 */
-    white-space: -o-pre-wrap;    /* Opera 7 */
-    word-wrap: break-word;       /* Internet Explorer 5.5+ */
+    white-space: pre-wrap;
     overflow: auto;
     overflow-y: hidden;
     font-size: 12px;
@@ -58,37 +53,92 @@
       left: 70px;
     }
   }
+}
 
-  .build-widget {
-    padding: 10px;
-    background: $background-color;
-    margin-bottom: 20px;
-    border-radius: 4px;
+.build-header {
+  position: relative;
+  padding-right: 40px;
 
-    .title {
-      margin-top: 0;
-      color: #666;
-      line-height: 1.5;
-    }
-    .attr-name {
-      color: #777;
-    }
+  @media (min-width: $screen-sm-min) {
+    padding-right: 0;
   }
 
-  .alert-disabled {
-    background: $background-color;
+  a {
+    color: $gl-gray;
 
-    a {
-      color: #3084bb !important;
+    &:hover {
+      color: $gl-link-color;
+      text-decoration: none;
     }
   }
+
+  code {
+    color: $code-color;
+  }
+
+  .avatar {
+    float: none;
+    margin-right: 2px;
+    margin-left: 2px;
+  }
 }
 
 table.builds {
-
   .build-link {
     a {
       color: $gl-dark-link-color;
     }
   }
 }
+
+.build-trace {
+  background: $ci-output-bg;
+  color: $ci-text-color;
+  white-space: pre;
+  overflow-x: auto;
+  font-size: 12px;
+
+  .fa-refresh {
+    font-size: 24px;
+  }
+
+  .bash {
+    display: block;
+  }
+}
+
+.right-sidebar.build-sidebar {
+  padding-top: $gl-padding;
+  padding-bottom: $gl-padding;
+
+  &.right-sidebar-collapsed {
+    display: none;
+  }
+
+  .block {
+    width: 100%;
+  }
+
+  .build-sidebar-header {
+    padding-top: 0;
+
+    .gutter-toggle {
+      margin-top: 0;
+    }
+  }
+}
+
+.build-detail-row {
+  margin-bottom: 5px;
+}
+
+.build-light-text {
+  color: $gl-placeholder-color;
+}
+
+.build-gutter-toggle {
+  position: absolute;
+  top: 50%;
+  right: 0;
+  margin-top: -17px;
+}
diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss
index c2cd227571f3cfaf6521fbaed5e0054e7037efc5..fc3f214aba55e70009e3f28e674047800177cbc2 100644
--- a/app/assets/stylesheets/pages/commit.scss
+++ b/app/assets/stylesheets/pages/commit.scss
@@ -26,8 +26,28 @@
 
 .commit-info-row {
   margin-bottom: 10px;
+
+  &.commit-info-row-header {
+    line-height: 34px;
+
+    @media (min-width: $screen-sm-min) {
+      margin-bottom: 0;
+    }
+
+    .commit-options-dropdown-caret {
+      @media (max-width: $screen-sm) {
+        margin-left: 0;
+      }
+    }
+  }
+
   .avatar {
     @extend .avatar-inline;
+    margin-left: 0;
+
+    @media (min-width: $screen-sm-min) {
+      margin-left: 4px;
+    }
   }
   .commit-committer-link,
   .commit-author-link {
@@ -35,10 +55,6 @@
     font-weight: bold;
   }
 
-  .time_ago {
-    margin-left: 8px;
-  }
-
   .fa-clipboard {
     color: $dropdown-title-btn-color;
   }
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index c8c6bbde0846a9c8491e514cb7efcff25579259b..761e33f0df772e61dd43fe8bf0a51e3b5100b074 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -7,84 +7,111 @@
   margin-right: 9px;
 }
 
-.lists-separator {
-  margin: 10px 0;
-  border-color: #ddd;
+.commit-header {
+  padding: 5px 10px;
+  background-color: $background-color;
+  border-top: 1px solid #eee;
+  border-bottom: 1px solid #eee;
+  font-size: 14px;
+
+  &:first-child {
+    border-top-width: 0;
+  }
 }
 
-.commits-row {
-  ul {
-    margin: 0;
+.commit-row-title {
+  line-height: 1;
+  margin-bottom: 7px;
 
-    li.commit {
-      padding: 8px 0;
-    }
+  .notes_count {
+    float: right;
+    margin-right: 10px;
+  }
+
+  .str-truncated {
+    max-width: 70%;
   }
 
-  .commits-row-date {
-    font-size: 15px;
-    line-height: 20px;
-    margin-bottom: 5px;
+  .commit-row-message {
+    color: $gl-dark-link-color;
+  }
+
+  .text-expander {
+    display: inline-block;
+    background: $gray-light;
+    color: $gl-placeholder-color;
+    padding: 0 5px;
+    cursor: pointer;
+    border: 1px solid $border-gray-dark;
+    border-radius: $border-radius-default;
+    margin-left: 5px;
+
+    &:hover {
+      background-color: darken($gray-light, 10%);
+      text-decoration: none;
+    }
   }
 }
 
-li.commit {
-  list-style: none;
+.commit-actions {
+  @media (min-width: $screen-sm-min) {
+    float: right;
+    margin-left: $gl-padding;
+    margin-top: 2px;
+    font-size: 0;
+  }
 
-  .commit-row-title {
-    font-size: $list-font-size;
-    line-height: 20px;
-    margin-bottom: 2px;
+  .btn-transparent {
+    padding-left: 0;
+    padding-right: 0;
+  }
 
-    .btn-clipboard {
-      margin-top: -1px;
+  .btn {
+    &:not(:first-child) {
+      margin-left: $gl-padding;
     }
+  }
+}
 
-    .notes_count {
-      float: right;
-      margin-right: 10px;
-    }
+.commit-short-id {
+  font-family: $monospace_font;
+  font-weight: 600;
+}
 
-    .commit_short_id {
-      min-width: 65px;
-      color: $gl-dark-link-color;
-      font-family: $monospace_font;
-    }
+.commit {
+  padding: 10px 0;
 
-    .str-truncated {
-      max-width: 70%;
-    }
+  @media (min-width: $screen-sm-min) {
+    padding-left: 46px;
+  }
 
-    .commit-row-message {
-      color: $gl-dark-link-color;
+  &:not(:last-child) {
+    border-bottom: 1px solid #eee;
+  }
 
-      &:hover {
-        text-decoration: underline;
-      }
-    }
+  a,
+  button {
+    color: $gl-dark-link-color;
+    vertical-align: baseline;
+  }
 
-    .text-expander {
-      background: #eee;
-      color: #555;
-      padding: 0 5px;
-      cursor: pointer;
-      margin-left: 4px;
-      &:hover {
-        background-color: #ddd;
-      }
-    }
+  .avatar {
+    margin-left: -46px;
   }
 
   .item-title {
     display: inline-block;
-    max-width: 70%;
+
+    @media (min-width: $screen-sm-min) {
+      max-width: 70%;
+    }
   }
 
   .commit-row-description {
     font-size: 14px;
     border-left: 1px solid #eee;
     padding: 10px 15px;
-    margin: 5px 0 10px 5px;
+    margin: 10px 0;
     background: #f9f9f9;
     display: none;
 
@@ -93,6 +120,7 @@ li.commit {
       background: inherit;
       padding: 0;
       margin: 0;
+      white-space: pre-wrap;
     }
 
     a {
@@ -102,7 +130,7 @@ li.commit {
 
   .commit-row-info {
     color: $gl-gray;
-    line-height: 24px;
+    line-height: 1;
 
     a {
       color: $gl-gray;
@@ -111,10 +139,6 @@ li.commit {
     .avatar {
       margin-right: 8px;
     }
-
-    .committed_ago {
-      display: inline-block;
-    }
   }
 
   &.inline-commit {
diff --git a/app/assets/stylesheets/pages/confirmation.scss b/app/assets/stylesheets/pages/confirmation.scss
index 125f495d6d4577e4d3ee03f3f14d4e833beeb10d..292225c52617979b1e07174999f99fe57a77cd22 100644
--- a/app/assets/stylesheets/pages/confirmation.scss
+++ b/app/assets/stylesheets/pages/confirmation.scss
@@ -2,13 +2,21 @@
   margin-bottom: 20px;
   border-bottom: 1px solid #eee;
 
-  > h1 {
+  > h1, h2, h3, h4, h5, h6 {
     font-weight: 400;
   }
 
   .lead {
     margin-bottom: 20px;
   }
+
+  ul, ol {
+    padding-left: 0;
+  }
+
+  li {
+    list-style-type: none;
+  }
 }
 
 .confirmation-content {
diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss
index 5e61e61d85cadd5e856db2cfe1e513310210d2d3..1b389d83525da3cd5e2bfcad042bd1cda135218d 100644
--- a/app/assets/stylesheets/pages/detail_page.scss
+++ b/app/assets/stylesheets/pages/detail_page.scss
@@ -29,8 +29,6 @@
     margin-top: 6px;
 
     p {
-      overflow-x: auto;
-
       &:last-child {
         margin-bottom: 0;
       }
diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss
index 8981f070a20337d9184f73b2a4d37c4cbd6015ee..22679c764dc8815d33c66c6b7aa43cae2af83dd1 100644
--- a/app/assets/stylesheets/pages/editor.scss
+++ b/app/assets/stylesheets/pages/editor.scss
@@ -23,7 +23,7 @@
   .file-title {
     @extend .monospace;
 
-    line-height: 42px;
+    line-height: 35px;
     padding-top: 7px;
     padding-bottom: 7px;
 
@@ -43,7 +43,7 @@
 
   .editor-file-name {
     @extend .monospace;
-    
+
     float: left;
     margin-right: 10px;
   }
@@ -59,7 +59,22 @@
   }
 
   .encoding-selector,
-  .license-selector {
+  .license-selector,
+  .gitignore-selector {
     display: inline-block;
+    vertical-align: top;
+    font-family: $regular_font;
+  }
+
+  .gitignore-selector {
+
+    .dropdown {
+      line-height: 21px;
+    }
+
+    .dropdown-menu-toggle {
+      vertical-align: top;
+      width: 220px;
+    }
   }
 }
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e160d676e35c3dffbce4d19e3cbca2c3779b6fac
--- /dev/null
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -0,0 +1,5 @@
+.environments {
+  .commit-title {
+    margin: 0;
+  }
+}
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index 1a813bcfc3b2436906b452cff3f878dfbe7e65e6..eafc24fb3654b4c5139d877a8541d34727736b97 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -45,6 +45,23 @@
   }
 }
 
+.groups-cover-block {
+
+  .container-fluid {
+    position: relative;
+  }
+
+  .access-request-button {
+    @include btn-gray;
+    position: absolute;
+    right: 16px;
+    bottom: 32px;
+    padding: 3px 10px;
+    text-transform: none;
+    background-color: $background-color;
+  }
+}
+
 .ldap-group-links {
   .form-actions {
     margin-bottom: $gl-padding;
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index d06086a581b05524480444a1c347438a7bfb1ebc..687117233f632162f076b31aebfac93a4098402b 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -29,11 +29,15 @@
   }
 }
 
-.issuable-sidebar {
+.right-sidebar {
   a {
     color: inherit;
   }
 
+  .issuable-header-text {
+    margin-top: 7px;
+  }
+
   .block {
     @include clearfix;
     padding: $gl-padding 0;
@@ -60,10 +64,6 @@
       margin-top: 0;
     }
 
-    .issuable-count {
-      margin-top: 7px;
-    }
-
     .gutter-toggle {
       margin-left: 20px;
       padding-left: 10px;
@@ -74,6 +74,10 @@
     }
   }
 
+  .block-first {
+    padding-top: 0;
+  }
+
   .title {
     color: $gl-text-color;
     margin-bottom: 10px;
@@ -141,7 +145,6 @@
 
       .assign-yourself {
         margin-top: 10px;
-        font-weight: normal;
         display: block;
       }
     }
@@ -150,6 +153,14 @@
       font-weight: 600;
     }
 
+    .light {
+      font-weight: normal;
+    }
+
+    .no-value {
+      color: $gl-placeholder-color;
+    }
+
     .sidebar-collapsed-icon {
       display: none;
     }
@@ -240,11 +251,16 @@
       padding-bottom: 0;
       margin-bottom: 10px;
     }
+
+    .issuable-header-btn {
+      display: none;
+    }
   }
 
-  .issuable-pager {
+  .issuable-header-btn {
     background: $gray-normal;
     border: 1px solid $border-gray-normal;
+    
     &:hover {
       background: $gray-dark;
       border: 1px solid $border-gray-dark;
@@ -255,7 +271,7 @@
     }
   }
 
-  a:not(.issuable-pager) {
+  a {
     &:hover {
       color: $md-link-color;
       text-decoration: none;
@@ -314,7 +330,7 @@
   margin-left: 5px;
 
   a {
-    color: #8c8c8c;
+    color: $gl-placeholder-color;
   }
 }
 
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index fc9db97132dd463b252b09c2ac0472ccac9c88a5..4e35ca329e465d291e10557af97522cc28a689e0 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -40,11 +40,6 @@
   }
 }
 
-.issue-search-form {
-  margin: 0;
-  height: 24px;
-}
-
 form.edit-issue {
   margin: 0;
 }
@@ -96,8 +91,3 @@ form.edit-issue {
 .issue-form .select2-container {
   width: 250px !important;
 }
-
-.issue-closed-by-widget {
-  color: $gl-text-color;
-  margin-left: 52px;
-}
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index e179bdf0048d1620d9f6803c4cf8c365ea942551..046c38aba44c65e51abf7fd5fe13645690dd338d 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -50,11 +50,26 @@
 
 .label-row {
   .label-name {
-    display: inline-block;
-    width: 200px;
+    display: block;
+    margin-bottom: 10px;
 
-    @media (max-width: $screen-xs-min) {
-      display: block;
+    @media (min-width: $screen-sm-min) {
+      display: inline-block;
+      width: 200px;
+      margin-bottom: 0;
+    }
+  }
+
+  .label-description {
+    display: block;
+    margin-bottom: 10px;
+
+    @media (min-width: $screen-sm-min) {
+      display: inline-block;
+      width: 40%;
+      margin-left: 10px;
+      margin-bottom: 0;
+      vertical-align: middle;
     }
   }
 
@@ -68,10 +83,6 @@
   padding: 3px 4px;
 }
 
-.label-subscription {
-  display: inline-block;
-}
-
 .dropdown-labels-error {
   padding: 5px 10px;
   margin-bottom: 10px;
@@ -79,62 +90,109 @@
   color: $white-light;
 }
 
-@mixin labels-mobile {
-  @media (max-width: $screen-xs-min) {
-    display: block;
-    width: 100%;
-    margin-left: 0;
-    padding: 10px 0;
+.manage-labels-list {
+  .btn-action {
+    color: $gl-dark-link-color;
+
+    .fa {
+      font-size: 18px;
+      vertical-align: middle;
+    }
+
+    &:hover {
+      color: $gl-link-color;
+
+      &.remove-row {
+        color: $gl-danger;
+      }
+    }
   }
-}
 
+  .dropdown {
+    @media (min-width: $screen-sm-min) {
+      float: right;
+    }
+  }
+}
 
-.manage-labels-list {
+.draggable-handler {
+  display: inline-block;
+  opacity: 0;
+  transition: opacity .3s;
+  color: $gray-darkest;
+}
 
-  .prepend-left-10, .prepend-description-left {
-    display: inline-block;
-    width: 40%;
-    vertical-align: middle;
+.prioritized-labels {
+  margin-bottom: 30px;
 
-    @include labels-mobile;
+  .add-priority {
+    display: none;
+    color: $gray-light;
   }
 
-  .prepend-description-left {
-    width: 57%;
+  li:hover {
+    .draggable-handler {
+      display: inline-block;
+      opacity: 1;
+    }
+  }
+}
 
-    @include labels-mobile;
+.other-labels {
+  .remove-priority {
+    display: none;
   }
+}
 
-  .pull-info-right {
-    float: right;
+.toggle-priority {
+  display: inline-block;
+  vertical-align: middle;
 
-    @media (max-width: $screen-xs-min) {
-      float: none;
-    }
+  button {
+    border-color: transparent;
+    padding: 5px 8px;
+    vertical-align: top;
+    font-size: 14px;
 
-    .action-buttons {
+    &:hover {
       border-color: transparent;
-      padding: 6px;
-      color: $gl-text-color;
-
-      &.label-subscribe-button {
-        padding-left: 0;
-      }
     }
+  }
+}
 
-    i {
-      color: $gl-text-color;
+.filtered-labels {
+  .label-row {
+    &:not(:last-child) {
+      margin-right: 5px;
     }
+  }
 
-    .append-right-20 {
-      a {
-        color: $gl-text-color;
-      }
+  .label-remove {
+    border-left: 1px solid rgba(0, 0, 0, .1);
+    z-index: 3;
+  }
 
-      @media (max-width: $screen-xs-min) {
-        display: block;
-        margin-bottom: 10px;
-      }
+  .btn {
+    color: inherit;
+  }
+}
+
+.label-options-toggle {
+  width: 100%;
+}
+
+.label-subscribe-button {
+  .label-subscribe-button-loading {
+    display: none;
+  }
+
+  &.disabled {
+    .label-subscribe-button-icon {
+      display: none;
+    }
+
+    .label-subscribe-button-loading {
+      display: block;
     }
   }
 }
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index c4005ba1e69265308b44af7becf9cf909578bfb1..53bff508c72b351ab9d79806db1f0c301df5aa2c 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -41,7 +41,7 @@
       margin: 0;
       margin-left: 20px;
       padding: 5px;
-      padding-top: 12px;
+      padding-top: 8px;
       line-height: 20px;
 
       &.right {
@@ -79,11 +79,14 @@
     }
 
     &.ci-failed,
-    &.ci-canceled,
     &.ci-error {
       color: $gl-danger;
     }
 
+    &.ci-canceled {
+      color: $gl-gray;
+    }
+
     a.monospace {
       color: inherit;
     }
@@ -105,11 +108,39 @@
       font-size: 17px;
       margin: 5px 0;
       color: $gl-gray-dark;
+
+      &.has-conflicts .fa-exclamation-triangle {
+        color: $gl-warning;
+      }
+
     }
 
     p:last-child {
       margin-bottom: 0;
     }
+
+    @media (max-width: $screen-sm-max) {
+      h4 {
+        font-size: 15px;
+      }
+
+      p {
+        font-size: 13px;
+      }
+
+      .btn,
+      .btn-group,
+      .accept-action {
+        width: 100%;
+        margin-bottom: 4px;
+      }
+
+      .accept-control {
+        width: 100%;
+        text-align: center;
+        margin: 0;
+      }
+    }
   }
 
   .mr-widget-footer {
@@ -280,11 +311,15 @@
       background-color: $white-light;
       color: $gl-placeholder-color;
     }
+  }
+}
 
-    th,
-    td {
-      padding: 16px;
+.merged-buttons {
+  .btn {
+    float: left;
+
+    &:not(:last-child) {
+      margin-right: 10px;
     }
   }
 }
-
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 7fa13e66b436661bacc013d0c304f316b1fae91f..577dddae74141200b683dec2a2d0a6c2f3dc2787 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -87,6 +87,39 @@
   }
 }
 
+.md-header .nav-links {
+  display: flex;
+  display: -webkit-flex;
+  flex-flow: row wrap;
+  -webkit-flex-flow: row wrap;
+  width: 100%;
+
+  .pull-right {
+    // Flexbox quirk to make sure right-aligned items stay right-aligned.
+    margin-left: auto;
+  }
+}
+
+.confidential-issue-warning {
+  background-color: $gray-normal;
+  border-radius: 3px;
+  padding: 3px 12px;
+  margin: auto;
+  margin-top: 0;
+  text-align: center;
+  font-size: 13px;
+
+  @media (max-width: $screen-md-min) {
+    // On smaller devices the warning becomes the fourth item in the list,
+    // rather than centering, and grows to span the full width of the
+    // comment area.
+    order: 4;
+    -webkit-order: 4;
+    margin: 6px auto;
+    width: 100%;
+  }
+}
+
 .discussion-form {
   padding: $gl-padding-top $gl-padding;
   background-color: $white-light;
@@ -96,17 +129,8 @@
   display: none;
   font-size: 15px;
 
-  .form-actions {
-    padding-left: 20px;
-
-    .btn-save {
-      float: left;
-    }
-
-    .note-form-option {
-      float: left;
-      padding: 2px 0 0 25px;
-    }
+  .md-area {
+    background-color: #fff;
   }
 }
 
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index a3e1ac13a4374fe0a9d15e86f7fa417508a29693..35d728aec83f420581e418a042a52b8e9360d385 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -69,6 +69,10 @@ ul.notes {
 
       .note-edit-form {
         display: block;
+
+        &.current-note-edit-form + .note-awards {
+          display: none;
+        }
       }
     }
 
@@ -116,8 +120,47 @@ ul.notes {
       }
     }
 
+    .note-awards {
+      .js-awards-block {
+        padding: 2px;
+        margin-top: 10px;
+      }
+
+      .award-control {
+        font-size: 13px;
+        padding: 2px 5px;
+      }
+    }
+
     .note-header {
       padding-bottom: 3px;
+      padding-right: 20px;
+
+      @media (min-width: $screen-sm-min) {
+        padding-right: 0;
+      }
+
+      @media (max-width: $screen-xs-min) {
+        .inline {
+          display: block;
+        }
+      }
+    }
+
+    .note-emoji-button {
+      .fa-spinner {
+        display: none;
+      }
+
+      &.is-loading {
+        .fa-smile-o {
+          display: none;
+        }
+
+        .fa-spinner {
+          display: inline-block;
+        }
+      }
     }
 
   }
@@ -179,6 +222,8 @@ ul.notes {
 
 .discussion-header,
 .note-header {
+  position: relative;
+
   a {
     color: inherit;
 
@@ -215,6 +260,20 @@ ul.notes {
   color: $notes-action-color;
 }
 
+.note-actions {
+  position: absolute;
+  right: 0;
+  top: 0;
+
+  .note-action-button {
+    margin-left: 10px;
+  }
+
+  @media (min-width: $screen-sm-min) {
+    position: relative;
+  }
+}
+
 .discussion-actions {
   @media (max-width: $screen-md-max) {
     float: none;
@@ -228,8 +287,13 @@ ul.notes {
 
 .note-action-button {
   display: inline-block;
-  margin-left: 10px;
-  line-height: 24px;
+  margin-left: 0;
+  line-height: 20px;
+
+  @media (min-width: $screen-sm-min) {
+    margin-left: 10px;
+    line-height: 24px;
+  }
 
   .fa {
     color: $notes-action-color;
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 546176b65e4aa5527c8e62ba80d0499c1c4edf4f..6128868b670a43ff1f3d4a50ceb3a2ad1d45d08e 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -1,4 +1,24 @@
-.pipeline-stage {
-  overflow: hidden;
-  text-overflow: ellipsis;
+.pipelines {
+  .stage {
+    max-width: 100px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+
+  .duration, .finished_at {
+    margin: 4px 0;
+  }
+
+  .commit-title {
+    margin: 0;
+  }
+
+  .controls {
+    white-space: nowrap;
+  }
+
+  .btn {
+    margin: 4px;
+  }
 }
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index 843379a3f54191f9eb3f94c4383f76075782ee6d..167ab40d8816669220c691a44ad2b24bc6d40225 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -66,12 +66,6 @@
   }
 }
 
-.calendar-hint {
-  margin-top: -12px;
-  float: right;
-  font-size: 12px;
-}
-
 .profile-link-holder {
   display: inline;
 
@@ -134,14 +128,6 @@
   }
 }
 
-.change-username-title {
-  color: $gl-warning;
-}
-
-.remove-account-title {
-  color: $gl-danger;
-}
-
 .provider-btn-group {
   display: inline-block;
   margin-right: 10px;
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 87912cddda3eb6ead15eed17002e05e34da92c04..fc9d9191f65eb614e887d98c94752ae664660088 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -5,12 +5,14 @@
     font-weight: normal;
   }
 }
+
 .no-ssh-key-message, .project-limit-message {
   background-color: #f28d35;
   margin-bottom: 0;
 }
+
 .new_project,
-.edit_project {
+.edit-project {
   fieldset.features {
     .control-label {
       font-weight: normal;
@@ -18,16 +20,23 @@
   }
 }
 
-.project-name-holder {
-  .help-inline {
-    vertical-align: top;
-    padding: 7px;
-  }
-}
-
 .project-home-panel {
-  padding-bottom: 40px;
-  border-bottom: 1px solid $border-color;
+  background: $white-light;
+  text-align: left;
+  padding: 24px 0;
+
+  .container-fluid {
+    position: relative;
+
+    @media (min-width: $screen-md-max) {
+      .row {
+        display: flex;
+        -ms-flex-align: center;
+        -webkit-align-items: center;
+        -webkit-box-align: center;
+      }
+    }
+  }
 
   .cover-controls {
     .project-settings-dropdown {
@@ -43,21 +52,54 @@
     }
   }
 
-  .project-identicon-holder {
-    margin-bottom: 16px;
+  .cover-title {
+    margin-bottom: 0;
+  }
 
-    .avatar, .identicon {
-      margin: 0 auto;
-      float: none;
+  .project-image-container {
+    @include make-sm-column(1);
+    max-width: 86px;
+    min-width: 86px;
+    padding-right: 0;
+
+    @media (max-width: $screen-md-max) {
+      padding-left: 0;
+      margin: 0 0 10px;
+      max-width: none;
+      min-width: none;
+
+      .avatar.s70 {
+        margin: auto;
+      }
     }
+  }
 
-    .identicon {
-      @include border-radius(50%);
+  .project-info {
+    @include make-sm-column(10);
+
+    h1 {
+      font-size: 24px;
+      font-weight: normal;
+      margin: 0;
     }
+
+    .project-home-desc {
+      p {
+        margin: 0;
+      }
+    }
+  }
+
+  .identicon {
+    float: left;
+    @include border-radius(50%);
+  }
+
+  .avatar {
+    float: none;
   }
 
   .notifications-btn {
-    margin-top: -28px;
 
     .fa-bell {
       margin-right: 6px;
@@ -69,28 +111,45 @@
   }
 
   .project-repo-buttons {
-    margin-top: 20px;
-    margin-bottom: 0;
+    font-size: 0;
 
-    .count-buttons {
-      display: block;
-      margin-bottom: 20px;
-    }
+    .btn {
+      @include btn-gray;
+      padding: 3px 10px;
+      text-transform: none;
+      background-color: $background-color;
 
-    .clone-row {
-      .split-repo-buttons,
-      .project-clone-holder {
-        display: inline-block;
+      .fa {
+        color: $layout-link-gray;
       }
 
-      .split-repo-buttons {
-        margin: 0 12px;
+      .fa-caret-down {
+        margin-left: 3px;
       }
     }
 
-    .btn {
-      @include btn-gray;
-      text-transform: none;
+    .btn-group:not(:first-child):not(:last-child) > .btn {
+      border-top-right-radius: 3px;
+      border-bottom-right-radius: 3px;
+    }
+
+    form {
+      margin-left: 10px;
+    }
+
+    .count-buttons {
+      display: inline-block;
+      vertical-align: top;
+      margin-top: 16px;
+    }
+
+    .project-clone-holder {
+      display: inline-block;
+      margin-top: 16px;
+
+      input {
+        height: 29px;
+      }
     }
 
     .count-with-arrow {
@@ -140,14 +199,18 @@
         line-height: 13px;
         padding: $gl-vert-padding $gl-padding;
         letter-spacing: .4px;
-        padding: 10px 14px;
+        padding: 7px 14px;
         text-align: center;
         vertical-align: middle;
         touch-action: manipulation;
         cursor: pointer;
         background-image: none;
         white-space: nowrap;
-        margin: 0 11px 0 4px;
+        margin: 0 10px 0 4px;
+
+        a {
+          color: inherit;
+        }
 
         &:hover {
           background: #fff;
@@ -155,13 +218,44 @@
       }
     }
   }
+
+  .project-right-buttons {
+    position: absolute;
+    right: 16px;
+    bottom: 0;
+
+    @media (max-width: $screen-lg-min) {
+      top: 0;
+    }
+
+    .access-request-button {
+      position: absolute;
+      right: 0;
+      bottom: 61px;
+
+      @media (max-width: $screen-lg-min) {
+        position: relative;
+        bottom: 0;
+        margin-right: 10px;
+      }
+    }
+  }
+
+  @media (max-width: $screen-md-max) {
+    text-align: center;
+
+    .project-info,
+    .project-image-container {
+      width: 100%;
+    }
+  }
 }
 
 .split-one {
   display: inline-table;
   margin-right: 12px;
 
-  a {
+  > a {
     margin: -1px;
   }
 }
@@ -194,10 +288,6 @@
   color: #555;
 }
 
-.project_member_row form {
-  margin: 0;
-}
-
 .transfer-project .select2-container {
   min-width: 200px;
 }
@@ -281,15 +371,16 @@ a.deploy-project-label {
 
 .project-import .btn {
   float: left;
+  margin-bottom: 10px;
   margin-right: 10px;
 }
 
 .project-stats {
-  text-align: center;
   margin-top: $gl-padding;
   margin-bottom: 0;
-  padding-top: 10px;
-  padding-bottom: 4px;
+  padding: 16px 0;
+  background-color: $white-light;
+  font-size: 0;
 
   ul.nav {
     display: inline-block;
@@ -300,12 +391,11 @@ a.deploy-project-label {
   }
 
   .nav > li > a {
-    @include btn-default;
-    @include btn-gray;
-
     background-color: transparent;
-    border: 1px solid #f7f8fa;
-    margin-left: 12px;
+    margin-right: 12px;
+    padding: 0 10px;
+    font-size: 15px;
+    color: $notes-light-color;
   }
 
   li {
@@ -325,6 +415,10 @@ a.deploy-project-label {
       background-color: #f0f2f5;
     }
   }
+
+  &.row-content-block.second-block {
+    margin-top: 0;
+  }
 }
 
 pre.light-well {
@@ -402,9 +496,11 @@ pre.light-well {
   margin: 0;
 }
 
-.project-show-activity {
-  .activity-filter-block {
-    margin-top: -1px;
+
+.activity-filter-block {
+  .controls {
+    padding-bottom: 10px;
+    border-bottom: 1px solid $border-color;
   }
 }
 
@@ -442,9 +538,14 @@ pre.light-well {
   border-top: 0;
 
   .edit-project-readme {
-    z-index: 100;
+    z-index: 2;
     position: relative;
   }
+
+  .wiki h1 {
+    border-bottom: none;
+    padding: 0;
+  }
 }
 
 .git-clone-holder {
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index 2bff70c8c6489c21afdda6d55cf3ead024aaadec..ae524cd6bae1a6a3c3fd767d71bb50ddf191ff40 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -28,6 +28,7 @@
   }
 
   .search-input {
+    padding-right: 20px;
     border: none;
     font-size: 14px;
     outline: none;
@@ -47,6 +48,7 @@
     display: inline-block;
     background-color: $location-badge-bg;
     vertical-align: top;
+    cursor: default;
   }
 
   .search-input-container {
@@ -55,7 +57,7 @@
     position: relative;
   }
 
-  .search-location-badge, .search-input-wrap {
+  .search-input-wrap {
     // Fallback if flexbox is not supported
     display: inline-block;
   }
@@ -156,13 +158,11 @@
 .search-holder {
   @media (min-width: $screen-sm-min) {
     display: -webkit-flex;
-    display: -ms-flexbox;
     display: flex;
   }
 
   .search-field-holder {
     -webkit-flex: 1 0 auto;
-    -ms-flex: 1 0 auto;
     flex: 1 0 auto;
     position: relative;
     margin-right: 0;
diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss
index 3fb700857135b5e479d2f6ef9009d80d1f41aa2c..2e8f356298d473fab7bc5d71ed11519ed1b4b1b8 100644
--- a/app/assets/stylesheets/pages/settings.scss
+++ b/app/assets/stylesheets/pages/settings.scss
@@ -12,3 +12,11 @@
   border: 1px solid $warning-message-border;
   border-radius: $border-radius-base;
 }
+
+.warning-title {
+  color: $gl-warning;
+}
+
+.danger-title {
+  color: $gl-danger;
+}
diff --git a/app/assets/stylesheets/pages/snippets.scss b/app/assets/stylesheets/pages/snippets.scss
index 639d639d5b0796aed4dc875879d5c30c4007f420..2aa939b7dc389197b6b17e747ad9ac68617b82e3 100644
--- a/app/assets/stylesheets/pages/snippets.scss
+++ b/app/assets/stylesheets/pages/snippets.scss
@@ -16,19 +16,6 @@
   }
 }
 
-.snippet-box {
-  @include border-radius(2px);
-
-  display: block;
-  float: left;
-  padding: 0 $gl-padding;
-  font-weight: normal;
-  margin-right: 10px;
-  font-size: $gl-font-size;
-  border: 1px solid;
-  line-height: 32px;
-}
-
 .markdown-snippet-copy {
   position: fixed;
   top: -10px;
@@ -36,3 +23,34 @@
   max-height: 0;
   max-width: 0;
 }
+
+.file-holder.snippet-file-content {
+  padding-bottom: $gl-padding;
+  border-bottom: 1px solid $border-color;
+
+  .file-title {
+    padding-top: $gl-padding;
+    padding-bottom: $gl-padding;
+  }
+
+  .file-actions {
+    top: 12px;
+  }
+
+  .file-content {
+    border-left: 1px solid $border-color;
+    border-right: 1px solid $border-color;
+    border-bottom: 1px solid $border-color;
+  }
+}
+
+.snippet-title {
+  font-size: 24px;
+  font-weight: normal;
+}
+
+.snippet-actions {
+  @media (min-width: $screen-sm-min) {
+    float: right;
+  }
+}
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index e51c3491dae714048642fbd46ea6da4bab679d97..afc00a68572189d5c6c018cfc341aa80e9a44a64 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -29,6 +29,17 @@
 .todo-item {
   .todo-title {
     @include str-truncated(calc(100% - 174px));
+    overflow: visible;
+  }
+
+  .status-box {
+    margin: 0;
+    float: none;
+    display: inline-block;
+    font-weight: normal;
+    padding: 0 5px;
+    line-height: inherit;
+    font-size: 14px;
   }
 
   .todo-body {
@@ -76,12 +87,11 @@
 
 @media (max-width: $screen-xs-max) {
   .todo-item {
-    padding-left: $gl-padding;
-
     .todo-title {
       white-space: normal;
       overflow: visible;
       max-width: 100%;
+      margin-bottom: 10px;
     }
 
     .avatar {
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index a84fc2e0318af423b4b3ff891ebd7cdae8612c4c..99c9e81ddb9acd69e4788756d14d97a6bfbb7b99 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -15,16 +15,23 @@
     margin-bottom: 0;
 
     tr {
-      > td, > th {
+      border-bottom: 1px solid $table-border-gray;
+      border-top: 1px solid $table-border-gray;
+
+      td, th {
         line-height: 23px;
       }
 
       &:hover {
+        cursor: pointer;
+
         td {
-          background: $row-hover;
+          background-color: $row-hover;
+          border-top: 1px solid $row-hover-border;
+          border-bottom: 1px solid $row-hover-border;
         }
-        cursor: pointer;
       }
+
       &.selected {
         td {
           background: $gray-dark;
@@ -94,7 +101,7 @@
   margin: 0;
 
   .commit {
-    padding: 0;
+    padding: 0 0 0 55px;
 
     .commit-row-title {
       .commit-row-message {
@@ -122,4 +129,6 @@
 .tree-controls {
   float: right;
   margin-top: 11px;
+  position: relative;
+  z-index: 2;
 }
diff --git a/app/assets/stylesheets/pages/xterm.scss b/app/assets/stylesheets/pages/xterm.scss
index 3f28e4029291ed83411b45b380c74f5e3148f00d..8d855ce99b021239eccac3ade77923b1132081e8 100644
--- a/app/assets/stylesheets/pages/xterm.scss
+++ b/app/assets/stylesheets/pages/xterm.scss
@@ -11,18 +11,15 @@
   $magenta: #cd00cd;
   $cyan: #00cdcd;
   $white: #e5e5e5;
-  $l-black: #7f7f7f;
-  $l-red: #f00;
-  $l-green: #0f0;
-  $l-yellow: #ff0;
-  $l-blue: #5c5cff;
-  $l-magenta: #f0f;
-  $l-cyan: #0ff;
-  $l-white: #fff;
+  $l-black: #373b41;
+  $l-red: #c66;
+  $l-green: #b5bd68;
+  $l-yellow: #f0c674;
+  $l-blue: #81a2be;
+  $l-magenta: #b294bb;
+  $l-cyan: #8abeb7;
+  $l-white: $ci-text-color;
 
-  .term-bold {
-    font-weight: bold;
-  }
   .term-italic {
     font-style: italic;
   }
diff --git a/app/controllers/admin/application_controller.rb b/app/controllers/admin/application_controller.rb
index cf795d977ce4ddf7b2d50e3fd98620e91bbd5406..35b2d91bfd32945ab6a0856978c64ae2889af275 100644
--- a/app/controllers/admin/application_controller.rb
+++ b/app/controllers/admin/application_controller.rb
@@ -3,9 +3,17 @@
 # Automatically sets the layout and ensures an administrator is logged in
 class Admin::ApplicationController < ApplicationController
   before_action :authenticate_admin!
+  before_action :display_geo_information
   layout 'admin'
 
   def authenticate_admin!
     render_404 unless current_user.is_admin?
   end
+
+  def display_geo_information
+    return unless Gitlab::Geo.secondary?
+
+    primary_node = view_context.link_to('primary node', Gitlab::Geo.primary_node.url)
+    flash.now[:notice] = "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the #{primary_node}.".html_safe
+  end
 end
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index a190c5a3ced41c3f7220e2ca52a75eb62049a7be..762a6d32319a1948b943d1cb4d8a71b0cab8ee36 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -42,6 +42,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
 
   def application_setting_params
     restricted_levels = params[:application_setting][:restricted_visibility_levels]
+
     if restricted_levels.nil?
       params[:application_setting][:restricted_visibility_levels] = []
     else
@@ -51,6 +52,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
     end
 
     import_sources = params[:application_setting][:import_sources]
+
     if import_sources.nil?
       params[:application_setting][:import_sources] = []
     else
@@ -74,6 +76,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
       :two_factor_grace_period,
       :gravatar_enabled,
       :sign_in_text,
+      :after_sign_up_text,
       :help_page_text,
       :home_page_url,
       :help_text,
@@ -109,6 +112,11 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
       :repository_checks_enabled,
       :metrics_packet_size,
       :send_user_confirmation_email,
+      :container_registry_token_expire_delay,
+      :elasticsearch_indexing,
+      :elasticsearch_search,
+      :elasticsearch_host,
+      :elasticsearch_port,
       restricted_visibility_levels: [],
       import_sources: [],
       disabled_oauth_sign_in_sources: []
diff --git a/app/controllers/admin/git_hooks_controller.rb b/app/controllers/admin/git_hooks_controller.rb
index ac726659044fc2d25072bfd3342684cf38a4d3f7..a3f8ecabcd9aa575a75ac6c349dabd3c11758d0c 100644
--- a/app/controllers/admin/git_hooks_controller.rb
+++ b/app/controllers/admin/git_hooks_controller.rb
@@ -10,7 +10,7 @@ class Admin::GitHooksController < Admin::ApplicationController
     @git_hook.update_attributes(git_hook_params.merge(is_sample: true))
 
     if @git_hook.valid?
-      redirect_to admin_git_hooks_path
+      redirect_to admin_git_hooks_path, notice: 'Git Hooks updated successfully.'
     else
       render :index
     end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 4fc57d080da0a023df448e99db3dd9f3b6ab1c80..830aacf903fd8e5df09576675d3eb82481a0fbb3 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -6,6 +6,7 @@ class ApplicationController < ActionController::Base
   include Gitlab::GonHelper
   include GitlabRoutingHelper
   include PageLayoutHelper
+  include WorkhorseHelper
 
   before_action :authenticate_user_from_token!
   before_action :authenticate_user!
@@ -186,8 +187,8 @@ class ApplicationController < ActionController::Base
   end
 
   def check_2fa_requirement
-    if two_factor_authentication_required? && current_user && !current_user.two_factor_enabled && !skip_two_factor?
-      redirect_to new_profile_two_factor_auth_path
+    if two_factor_authentication_required? && current_user && !current_user.two_factor_enabled? && !skip_two_factor?
+      redirect_to profile_two_factor_auth_path
     end
   end
 
@@ -236,7 +237,7 @@ class ApplicationController < ActionController::Base
   end
 
   def configure_permitted_parameters
-    devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:username, :email, :password, :login, :remember_me, :otp_attempt) }
+    devise_parameter_sanitizer.permit(:sign_in, keys: [:username, :email, :password, :login, :remember_me, :otp_attempt])
   end
 
   def hexdigest(string)
@@ -273,7 +274,7 @@ class ApplicationController < ActionController::Base
       # internal repos where you are not a member. Enable this filter
       # or improve current implementation to filter only issues you
       # created or assigned or mentioned
-      #@filter_params[:authorized_only] = true
+      # @filter_params[:authorized_only] = true
     end
 
     @filter_params
@@ -352,6 +353,10 @@ class ApplicationController < ActionController::Base
     session[:skip_tfa] && session[:skip_tfa] > Time.current
   end
 
+  def browser_supports_u2f?
+    browser.chrome? && browser.version.to_i >= 41 && !browser.device.mobile?
+  end
+
   def redirect_to_home_page_url?
     # If user is not signed-in and tries to access root_path - redirect him to landing page
     # Don't redirect to the default URL to prevent endless redirections
@@ -365,6 +370,13 @@ class ApplicationController < ActionController::Base
     current_user.nil? && root_path == request.path
   end
 
+  # U2F (universal 2nd factor) devices need a unique identifier for the application
+  # to perform authentication.
+  # https://developers.yubico.com/U2F/App_ID.html
+  def u2f_app_id
+    request.base_url
+  end
+
   private
 
   def set_default_sort
diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb
index f316fd8c0013ef193cc9b2b368e374f9ce8afcd0..d2ff371f0cf0c861143bc6505efcea8958b51514 100644
--- a/app/controllers/autocomplete_controller.rb
+++ b/app/controllers/autocomplete_controller.rb
@@ -39,6 +39,25 @@ class AutocompleteController < ApplicationController
     render json: @user, only: [:name, :username, :id], methods: [:avatar_url]
   end
 
+  def projects
+    project = Project.find_by_id(params[:project_id])
+
+    projects = current_user.authorized_projects
+    projects = projects.search(params[:search]) if params[:search].present?
+    projects = projects.select do |project|
+      current_user.can?(:admin_issue, project)
+    end
+
+    no_project = {
+      id: 0,
+      name_with_namespace: 'No project',
+    }
+    projects.unshift(no_project)
+    projects.delete(project)
+
+    render json: projects.to_json(only: [:id, :name_with_namespace], methods: :name_with_namespace)
+  end
+
   private
 
   def find_users
diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb
index d5918a7af3b02b214d443f692da0748a3fcc440f..998b8adc4112573e1da0b908955a6351f5f74f1c 100644
--- a/app/controllers/concerns/authenticates_with_two_factor.rb
+++ b/app/controllers/concerns/authenticates_with_two_factor.rb
@@ -24,7 +24,64 @@ module AuthenticatesWithTwoFactor
   # Returns nil
   def prompt_for_two_factor(user)
     session[:otp_user_id] = user.id
+    setup_u2f_authentication(user)
+    render 'devise/sessions/two_factor'
+  end
+
+  def authenticate_with_two_factor
+    user = self.resource = find_user
+
+    if user_params[:otp_attempt].present? && session[:otp_user_id]
+      authenticate_with_two_factor_via_otp(user)
+    elsif user_params[:device_response].present? && session[:otp_user_id]
+      authenticate_with_two_factor_via_u2f(user)
+    elsif user && user.valid_password?(user_params[:password])
+      prompt_for_two_factor(user)
+    end
+  end
+
+  private
+
+  def authenticate_with_two_factor_via_otp(user)
+    if valid_otp_attempt?(user)
+      # Remove any lingering user data from login
+      session.delete(:otp_user_id)
+
+      remember_me(user) if user_params[:remember_me] == '1'
+      sign_in(user)
+    else
+      flash.now[:alert] = 'Invalid two-factor code.'
+      render :two_factor
+    end
+  end
+
+  # Authenticate using the response from a U2F (universal 2nd factor) device
+  def authenticate_with_two_factor_via_u2f(user)
+    if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenges])
+      # Remove any lingering user data from login
+      session.delete(:otp_user_id)
+      session.delete(:challenges)
+
+      sign_in(user)
+    else
+      flash.now[:alert] = 'Authentication via U2F device failed.'
+      prompt_for_two_factor(user)
+    end
+  end
+
+  # Setup in preparation of communication with a U2F (universal 2nd factor) device
+  # Actual communication is performed using a Javascript API
+  def setup_u2f_authentication(user)
+    key_handles = user.u2f_registrations.pluck(:key_handle)
+    u2f = U2F::U2F.new(u2f_app_id)
 
-    render 'devise/sessions/two_factor' and return
+    if key_handles.present?
+      sign_requests = u2f.authentication_requests(key_handles)
+      challenges = sign_requests.map(&:challenge)
+      session[:challenges] = challenges
+      gon.push(u2f: { challenges: challenges, app_id: u2f_app_id,
+                      sign_requests: sign_requests,
+                      browser_supports_u2f: browser_supports_u2f? })
+    end
   end
 end
diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb
new file mode 100644
index 0000000000000000000000000000000000000000..225a0d620363b8cefbd018de82102d5a42657972
--- /dev/null
+++ b/app/controllers/concerns/membership_actions.rb
@@ -0,0 +1,67 @@
+module MembershipActions
+  extend ActiveSupport::Concern
+  include MembersHelper
+
+  def request_access
+    membershipable.request_access(current_user)
+
+    redirect_to polymorphic_path(membershipable),
+                notice: 'Your request for access has been queued for review.'
+  end
+
+  def approve_access_request
+    @member = membershipable.members.request.find(params[:id])
+
+    return render_403 unless can?(current_user, action_member_permission(:update, @member), @member)
+
+    @member.accept_request
+
+    log_audit_event(@member, action: :create)
+
+    redirect_to polymorphic_url([membershipable, :members])
+  end
+
+  def leave
+    @member = membershipable.members.find_by(user_id: current_user)
+    return render_403 unless @member
+
+    source_type = @member.real_source_type.humanize(capitalize: false)
+
+    if can?(current_user, action_member_permission(:destroy, @member), @member)
+      notice =
+        if @member.request?
+          "Your access request to the #{source_type} has been withdrawn."
+        else
+          "You left the \"#{@member.source.human_name}\" #{source_type}."
+        end
+      @member.destroy
+
+      log_audit_event(@member, action: :destroy) unless @member.request?
+
+      redirect_to [:dashboard, @member.real_source_type.tableize], notice: notice
+    else
+      if cannot_leave?
+        alert = "You can not leave the \"#{@member.source.human_name}\" #{source_type}."
+        alert << " Transfer or delete the #{source_type}."
+        redirect_to polymorphic_url(membershipable), alert: alert
+      else
+        render_403
+      end
+    end
+  end
+
+  protected
+
+  def membershipable
+    raise NotImplementedError
+  end
+
+  def cannot_leave?
+    raise NotImplementedError
+  end
+
+  def log_audit_event(member, options = {})
+    AuditEventService.new(current_user, membershipable, options).
+      for_member(member).security_event
+  end
+end
diff --git a/app/controllers/concerns/toggle_award_emoji.rb b/app/controllers/concerns/toggle_award_emoji.rb
new file mode 100644
index 0000000000000000000000000000000000000000..036777c80c19f1edf6a29e19b4d53f91b6d6262b
--- /dev/null
+++ b/app/controllers/concerns/toggle_award_emoji.rb
@@ -0,0 +1,31 @@
+module ToggleAwardEmoji
+  extend ActiveSupport::Concern
+
+  included do
+    before_action :authenticate_user!, only: [:toggle_award_emoji]
+  end
+
+  def toggle_award_emoji
+    name = params.require(:name)
+
+    awardable.toggle_award_emoji(name, current_user)
+    TodoService.new.new_award_emoji(to_todoable(awardable), current_user)
+
+    render json: { ok: true }
+  end
+
+  private
+
+  def to_todoable(awardable)
+    case awardable
+    when Note
+      awardable.noteable
+    else
+      awardable
+    end
+  end
+
+  def awardable
+    raise NotImplementedError
+  end
+end
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index e46e77a198b260bac35f7962ce79162ef8608615..f5558181cde53a991b5acf2a4beb174415b33877 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -1,11 +1,13 @@
 class Groups::GroupMembersController < Groups::ApplicationController
+  include MembershipActions
+
   # Authorize
-  before_action :authorize_admin_group_member!, except: [:index, :leave]
+  before_action :authorize_admin_group_member!, except: [:index, :leave, :request_access]
 
   def index
     @project = @group.projects.find(params[:project_id]) if params[:project_id]
     @members = @group.group_members
-    @members = @members.non_invite unless can?(current_user, :admin_group, @group)
+    @members = @members.non_pending unless can?(current_user, :admin_group, @group)
 
     if params[:search].present?
       users = @group.users.search(params[:search]).to_a
@@ -71,31 +73,16 @@ class Groups::GroupMembersController < Groups::ApplicationController
     end
   end
 
-  def leave
-    @group_member = @group.group_members.find_by(user_id: current_user)
-
-    if can?(current_user, :destroy_group_member, @group_member)
-      @group_member.destroy
-      log_audit_event(@group_member, action: :destroy)
-
-      redirect_to(dashboard_groups_path, notice: "You left #{group.name} group.")
-    else
-      if @group.last_owner?(current_user)
-        redirect_to(dashboard_groups_path, alert: "You can not leave #{group.name} group because you're the last owner. Transfer or delete the group.")
-      else
-        return render_403
-      end
-    end
-  end
-
   protected
 
   def member_params
     params.require(:group_member).permit(:access_level, :user_id)
   end
 
-  def log_audit_event(member, options = {})
-    AuditEventService.new(current_user, @group, options).
-      for_member(member).security_event
+  # MembershipActions concern
+  alias_method :membershipable, :group
+
+  def cannot_leave?
+    @group.last_owner?(current_user)
   end
 end
diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb
index 156ab2811d66401eb7a3dde917a0496f42fefc4f..014b9b43ff26f955f969a883defd154817555721 100644
--- a/app/controllers/jwt_controller.rb
+++ b/app/controllers/jwt_controller.rb
@@ -32,7 +32,7 @@ class JwtController < ApplicationController
   end
 
   def auth_params
-    params.permit(:service, :scope, :offline_token, :account, :client_id)
+    params.permit(:service, :scope, :account, :client_id)
   end
 
   def authenticate_project(login, password)
@@ -42,46 +42,8 @@ class JwtController < ApplicationController
   end
 
   def authenticate_user(login, password)
-    # TODO: this is a copy and paste from grack_auth,
-    # it should be refactored in the future
-
-    user = Gitlab::Auth.new.find(login, password)
-
-    # If the user authenticated successfully, we reset the auth failure count
-    # from Rack::Attack for that IP. A client may attempt to authenticate
-    # with a username and blank password first, and only after it receives
-    # a 401 error does it present a password. Resetting the count prevents
-    # false positives from occurring.
-    #
-    # Otherwise, we let Rack::Attack know there was a failed authentication
-    # attempt from this IP. This information is stored in the Rails cache
-    # (Redis) and will be used by the Rack::Attack middleware to decide
-    # whether to block requests from this IP.
-    config = Gitlab.config.rack_attack.git_basic_auth
-
-    if config.enabled
-      if user
-        # A successful login will reset the auth failure count from this IP
-        Rack::Attack::Allow2Ban.reset(request.ip, config)
-      else
-        banned = Rack::Attack::Allow2Ban.filter(request.ip, config) do
-          # Unless the IP is whitelisted, return true so that Allow2Ban
-          # increments the counter (stored in Rails.cache) for the IP
-          if config.ip_whitelist.include?(request.ip)
-            false
-          else
-            true
-          end
-        end
-
-        if banned
-          Rails.logger.info "IP #{request.ip} failed to login " \
-              "as #{login} but has been temporarily banned from Git auth"
-          return
-        end
-      end
-    end
-
+    user = Gitlab::Auth.find_with_user_password(login, password)
+    Gitlab::Auth.rate_limit!(request.ip, success: user.present?, login: login)
     user
   end
 end
diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb
index c6bdd0602c156f916b14bdf03d429f44af4350c3..0f54dfa4efc898ada20bfb20dd0c3d1819692c05 100644
--- a/app/controllers/oauth/applications_controller.rb
+++ b/app/controllers/oauth/applications_controller.rb
@@ -32,7 +32,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
   def verify_user_oauth_applications_enabled
     return if current_application_settings.user_oauth_applications?
 
-    redirect_to applications_profile_url
+    redirect_to profile_path
   end
 
   def set_index_vars
diff --git a/app/controllers/oauth/geo_auth_controller.rb b/app/controllers/oauth/geo_auth_controller.rb
index c74017e8b07b54174a09004ef40b9fa7eefd6c2c..6e9f2f984b96d361b37b97b1668949afa20c9aaa 100644
--- a/app/controllers/oauth/geo_auth_controller.rb
+++ b/app/controllers/oauth/geo_auth_controller.rb
@@ -25,9 +25,7 @@ class Oauth::GeoAuthController < ActionController::Base
     user = User.find_by(id: remote_user['id'])
 
     if user && sign_in(user, bypass: true)
-      session[:access_token] = token
-      return_to = oauth.get_oauth_state_return_to
-      redirect_to(return_to || root_path)
+      after_sign_in_with_gitlab(token, oauth.get_oauth_state_return_to)
     else
       invalid_credentials
     end
@@ -46,6 +44,16 @@ class Oauth::GeoAuthController < ActionController::Base
 
   private
 
+  def after_sign_in_with_gitlab(token, return_to)
+    if Gitlab::Geo.primary_node
+      primary_node = view_context.link_to('primary node', Gitlab::Geo.primary_node.url)
+      flash[:notice] = "You are on a secondary (read-only) Geo node. If you want to make any changes, you must visit the #{primary_node}.".html_safe
+    end
+
+    session[:access_token] = token
+    redirect_to(return_to || root_path)
+  end
+
   def invalid_credentials
     @error = 'Cannot find user to login. Your account may have been deleted.'
     render :error, layout: 'errors'
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index df98f56a1cd16ff4e5e735d506766c51d9dc1f20..f35d631df0cea82e76bf7f824ab52be3c028d79e 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -97,7 +97,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
     handle_signup_error
   end
 
-  def handle_service_ticket provider, ticket
+  def handle_service_ticket(provider, ticket)
     Gitlab::OAuth::Session.create provider, ticket
     session[:service_tickets] ||= {}
     session[:service_tickets][provider] = ticket
diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb
index 18ee55c839a649f11c787829119e0c637d40351c..40d1906a53f4bd5838aed40e4b42fa983794b5bf 100644
--- a/app/controllers/profiles/notifications_controller.rb
+++ b/app/controllers/profiles/notifications_controller.rb
@@ -1,12 +1,13 @@
 class Profiles::NotificationsController < Profiles::ApplicationController
   def show
-    @user = current_user
-    @group_notifications = current_user.notification_settings.for_groups
-    @project_notifications = current_user.notification_settings.for_projects
+    @user                        = current_user
+    @group_notifications         = current_user.notification_settings.for_groups
+    @project_notifications       = current_user.notification_settings.for_projects
+    @global_notification_setting = current_user.global_notification_setting
   end
 
   def update
-    if current_user.update_attributes(user_params)
+    if current_user.update_attributes(user_params) && update_notification_settings
       flash[:notice] = "Notification settings saved"
     else
       flash[:alert] = "Failed to save new settings"
@@ -16,6 +17,18 @@ class Profiles::NotificationsController < Profiles::ApplicationController
   end
 
   def user_params
-    params.require(:user).permit(:notification_email, :notification_level)
+    params.require(:user).permit(:notification_email)
+  end
+
+  def global_notification_setting_params
+    params.require(:global_notification_setting).permit(:level)
+  end
+
+  private
+
+  def update_notification_settings
+    return true unless global_notification_setting_params
+
+    current_user.global_notification_setting.update_attributes(global_notification_setting_params)
   end
 end
diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb
index 8f83fdd02bc71c256564ac40e6731415021e85ce..6a358fdcc0583abeba2ae418817c49b848d5a038 100644
--- a/app/controllers/profiles/two_factor_auths_controller.rb
+++ b/app/controllers/profiles/two_factor_auths_controller.rb
@@ -1,7 +1,7 @@
 class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
   skip_before_action :check_2fa_requirement
 
-  def new
+  def show
     unless current_user.otp_secret
       current_user.otp_secret = User.generate_otp_secret(32)
     end
@@ -12,21 +12,22 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
 
     current_user.save! if current_user.changed?
 
-    if two_factor_authentication_required?
+    if two_factor_authentication_required? && !current_user.two_factor_enabled?
       if two_factor_grace_period_expired?
-        flash.now[:alert] = 'You must enable Two-factor Authentication for your account.'
+        flash.now[:alert] = 'You must enable Two-Factor Authentication for your account.'
       else
         grace_period_deadline = current_user.otp_grace_period_started_at + two_factor_grace_period.hours
-        flash.now[:alert] = "You must enable Two-factor Authentication for your account before #{l(grace_period_deadline)}."
+        flash.now[:alert] = "You must enable Two-Factor Authentication for your account before #{l(grace_period_deadline)}."
       end
     end
 
     @qr_code = build_qr_code
+    setup_u2f_registration
   end
 
   def create
     if current_user.validate_and_consume_otp!(params[:pin_code])
-      current_user.two_factor_enabled = true
+      current_user.otp_required_for_login = true
       @codes = current_user.generate_otp_backup_codes!
       current_user.save!
 
@@ -34,8 +35,23 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
     else
       @error = 'Invalid pin code'
       @qr_code = build_qr_code
+      setup_u2f_registration
+      render 'show'
+    end
+  end
+
+  # A U2F (universal 2nd factor) device's information is stored after successful
+  # registration, which is then used while 2FA authentication is taking place.
+  def create_u2f
+    @u2f_registration = U2fRegistration.register(current_user, u2f_app_id, params[:device_response], session[:challenges])
 
-      render 'new'
+    if @u2f_registration.persisted?
+      session.delete(:challenges)
+      redirect_to profile_account_path, notice: "Your U2F device was registered!"
+    else
+      @qr_code = build_qr_code
+      setup_u2f_registration
+      render :show
     end
   end
 
@@ -70,4 +86,21 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
   def issuer_host
     Gitlab.config.gitlab.host
   end
+
+  # Setup in preparation of communication with a U2F (universal 2nd factor) device
+  # Actual communication is performed using a Javascript API
+  def setup_u2f_registration
+    @u2f_registration ||= U2fRegistration.new
+    @registration_key_handles = current_user.u2f_registrations.pluck(:key_handle)
+    u2f = U2F::U2F.new(u2f_app_id)
+
+    registration_requests = u2f.registration_requests
+    sign_requests = u2f.authentication_requests(@registration_key_handles)
+    session[:challenges] = registration_requests.map(&:challenge)
+
+    gon.push(u2f: { challenges: session[:challenges], app_id: u2f_app_id,
+                    register_requests: registration_requests,
+                    sign_requests: sign_requests,
+                    browser_supports_u2f: browser_supports_u2f? })
+  end
 end
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index be872a93feee0c8251485422590d159ba421db8d..776ba92c9abe52eb806629f8c9f3e5c0578fd3cf 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -26,7 +26,7 @@ class Projects::ApplicationController < ApplicationController
       project_path = "#{namespace}/#{id}"
       @project = Project.find_with_namespace(project_path)
 
-      if @project && can?(current_user, :read_project, @project)
+      if can?(current_user, :read_project, @project) && !@project.pending_delete?
         if @project.path_with_namespace != project_path
           redirect_to request.original_url.gsub(project_path, @project.path_with_namespace)
         end
diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb
index cfea12665163db0f29e8e24e36b4812f10df0642..f11c8321464e1b49876f80cc591311408541b712 100644
--- a/app/controllers/projects/artifacts_controller.rb
+++ b/app/controllers/projects/artifacts_controller.rb
@@ -1,22 +1,18 @@
 class Projects::ArtifactsController < Projects::ApplicationController
   layout 'project'
   before_action :authorize_read_build!
+  before_action :authorize_update_build!, only: [:keep]
+  before_action :validate_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)
 
@@ -34,10 +30,19 @@ class Projects::ArtifactsController < Projects::ApplicationController
     end
   end
 
+  def keep
+    build.keep_artifacts!
+    redirect_to namespace_project_build_path(project.namespace, project, build)
+  end
+
   private
 
+  def validate_artifacts!
+    render_404 unless build.artifacts?
+  end
+
   def build
-    @build ||= project.builds.unscoped.find_by!(id: params[:build_id])
+    @build ||= project.builds.find_by!(id: params[:build_id])
   end
 
   def artifacts_file
diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb
index 72921b3aa145303f0246614c90484fe686b08558..5962f74c39bfa431debf2732d265efd00fee2e4d 100644
--- a/app/controllers/projects/avatars_controller.rb
+++ b/app/controllers/projects/avatars_controller.rb
@@ -10,10 +10,7 @@ class Projects::AvatarsController < Projects::ApplicationController
 
       return if cached_blob?
 
-      headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob))
-      headers['Content-Disposition'] = 'inline'
-      headers['Content-Type'] = safe_content_type(@blob)
-      head :ok # 'render nothing: true' messes up the Content-Type
+      send_git_blob @repository, @blob
     else
       render_404
     end
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index d09e7375b6740a8c73994055924b418ab6d527a6..dd9508da04912b9a154ef25976e9e9d026963ae9 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -50,7 +50,7 @@ class Projects::BranchesController < Projects::ApplicationController
         redirect_to namespace_project_branches_path(@project.namespace,
                                                     @project), status: 303
       end
-      format.js { render status: status[:return_code] }
+      format.js { render nothing: true, status: status[:return_code] }
     end
   end
 
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index bb1f6c5e9809698393ff353fc547e25a91d2045b..ef3051d7519c8e4d551b04b13cf7367bab34e4cf 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -26,9 +26,9 @@ class Projects::BuildsController < Projects::ApplicationController
   end
 
   def show
-    @builds = @project.ci_commits.find_by_sha(@build.sha).builds.order('id DESC')
+    @builds = @project.pipelines.find_by_sha(@build.sha).builds.order('id DESC')
     @builds = @builds.where("id not in (?)", @build.id)
-    @commit = @build.commit
+    @pipeline = @build.pipeline
 
     respond_to do |format|
       format.html
@@ -41,7 +41,7 @@ class Projects::BuildsController < Projects::ApplicationController
   def trace
     respond_to do |format|
       format.json do
-        render json: @build.trace_with_state(params[:state]).merge!(id: @build.id, status: @build.status)
+        render json: @build.trace_with_state(params[:state].presence).merge!(id: @build.id, status: @build.status)
       end
     end
   end
@@ -51,7 +51,7 @@ class Projects::BuildsController < Projects::ApplicationController
       return render_404
     end
 
-    build = Ci::Build.retry(@build)
+    build = Ci::Build.retry(@build, current_user)
     redirect_to build_path(build)
   end
 
@@ -81,7 +81,7 @@ class Projects::BuildsController < Projects::ApplicationController
   private
 
   def build
-    @build ||= project.builds.unscoped.find_by!(id: params[:id])
+    @build ||= project.builds.find_by!(id: params[:id])
   end
 
   def build_path(build)
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 10b5932affabb60049a2fc3fc5164280fd7cd33d..6751737d15ea7d7e00a4c1d5ba7af2c4f36d6153 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -46,7 +46,7 @@ class Projects::CommitController < Projects::ApplicationController
   def retry_builds
     ci_builds.latest.failed.each do |build|
       if build.retryable?
-        Ci::Build.retry(build)
+        Ci::Build.retry(build, current_user)
       end
     end
 
@@ -99,12 +99,12 @@ class Projects::CommitController < Projects::ApplicationController
     @commit ||= @project.commit(params[:id])
   end
 
-  def ci_commits
-    @ci_commits ||= project.ci_commits.where(sha: commit.sha)
+  def pipelines
+    @pipelines ||= project.pipelines.where(sha: commit.sha)
   end
 
   def ci_builds
-    @ci_builds ||= Ci::Build.where(commit: ci_commits)
+    @ci_builds ||= Ci::Build.where(pipeline: pipelines)
   end
 
   def define_show_vars
@@ -117,8 +117,8 @@ class Projects::CommitController < Projects::ApplicationController
     @diff_refs = [commit.parent || commit, commit]
     @notes_count = commit.notes.count
 
-    @statuses = CommitStatus.where(commit: ci_commits)
-    @builds = Ci::Build.where(commit: ci_commits)
+    @statuses = CommitStatus.where(pipeline: pipelines)
+    @builds = Ci::Build.where(pipeline: pipelines)
   end
 
   def assign_change_commit_vars(mr_source_branch)
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4b4337961612c0fd7df7d8c0dfc5e9a60c201d13
--- /dev/null
+++ b/app/controllers/projects/environments_controller.rb
@@ -0,0 +1,49 @@
+class Projects::EnvironmentsController < Projects::ApplicationController
+  layout 'project'
+  before_action :authorize_read_environment!
+  before_action :authorize_create_environment!, only: [:new, :create]
+  before_action :authorize_update_environment!, only: [:destroy]
+  before_action :environment, only: [:show, :destroy]
+
+  def index
+    @environments = project.environments
+  end
+
+  def show
+    @deployments = environment.deployments.order(id: :desc).page(params[:page])
+  end
+
+  def new
+    @environment = project.environments.new
+  end
+
+  def create
+    @environment = project.environments.create(create_params)
+
+    if @environment.persisted?
+      redirect_to namespace_project_environment_path(project.namespace, project, @environment)
+    else
+      render 'new'
+    end
+  end
+
+  def destroy
+    if @environment.destroy
+      flash[:notice] = 'Environment was successfully removed.'
+    else
+      flash[:alert] = 'Failed to remove environment.'
+    end
+
+    redirect_to namespace_project_environments_path(project.namespace, project)
+  end
+
+  private
+
+  def create_params
+    params.require(:environment).permit(:name)
+  end
+
+  def environment
+    @environment ||= project.environments.find(params[:id])
+  end
+end
diff --git a/app/controllers/projects/find_file_controller.rb b/app/controllers/projects/find_file_controller.rb
index 54a0c447aee9928506dc65b1bb65017ebd30af83..cf53ad0a670afc87baa90ace785696ac567e772c 100644
--- a/app/controllers/projects/find_file_controller.rb
+++ b/app/controllers/projects/find_file_controller.rb
@@ -1,26 +1,26 @@
-# Controller for viewing a repository's file structure
-class Projects::FindFileController < Projects::ApplicationController
-  include ExtractsPath
-  include ActionView::Helpers::SanitizeHelper
-  include TreeHelper
-
-  before_action :require_non_empty_project
-  before_action :assign_ref_vars
-  before_action :authorize_download_code!
-
-  def show
-    return render_404 unless @repository.commit(@ref)
-
-    respond_to do |format|
-      format.html
-    end
-  end
-
-  def list
-    file_paths = @repo.ls_files(@ref)
-
-    respond_to do |format|
-      format.json { render json: file_paths }
-    end
-  end
-end
+# Controller for viewing a repository's file structure
+class Projects::FindFileController < Projects::ApplicationController
+  include ExtractsPath
+  include ActionView::Helpers::SanitizeHelper
+  include TreeHelper
+
+  before_action :require_non_empty_project
+  before_action :assign_ref_vars
+  before_action :authorize_download_code!
+
+  def show
+    return render_404 unless @repository.commit(@ref)
+
+    respond_to do |format|
+      format.html
+    end
+  end
+
+  def list
+    file_paths = @repo.ls_files(@ref)
+
+    respond_to do |format|
+      format.json { render json: file_paths }
+    end
+  end
+end
diff --git a/app/controllers/projects/git_hooks_controller.rb b/app/controllers/projects/git_hooks_controller.rb
index 27c90df871b72682960c8281a5ddb69b21d3c4c6..3770aba26d2283d6123ff3609eb33e854e754d1d 100644
--- a/app/controllers/projects/git_hooks_controller.rb
+++ b/app/controllers/projects/git_hooks_controller.rb
@@ -17,7 +17,7 @@ class Projects::GitHooksController < Projects::ApplicationController
     @git_hook.update_attributes(git_hook_params)
 
     if @git_hook.valid?
-      redirect_to namespace_project_git_hooks_path(@project.namespace, @project)
+      redirect_to namespace_project_git_hooks_path(@project.namespace, @project), notice: 'Git Hooks updated successfully.'
     else
       render :index
     end
diff --git a/app/controllers/projects/git_http_controller.rb b/app/controllers/projects/git_http_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f907d63258b19e20e27be07f83c365dde9f5cd67
--- /dev/null
+++ b/app/controllers/projects/git_http_controller.rb
@@ -0,0 +1,147 @@
+class Projects::GitHttpController < Projects::ApplicationController
+  attr_reader :user
+
+  # Git clients will not know what authenticity token to send along
+  skip_before_action :verify_authenticity_token
+  skip_before_action :repository
+  before_action :authenticate_user
+  before_action :ensure_project_found!
+
+  # GET /foo/bar.git/info/refs?service=git-upload-pack (git pull)
+  # GET /foo/bar.git/info/refs?service=git-receive-pack (git push)
+  def info_refs
+    if upload_pack? && upload_pack_allowed?
+      render_ok
+    elsif receive_pack? && receive_pack_allowed?
+      render_ok
+    else
+      render_not_found
+    end
+  end
+
+  # POST /foo/bar.git/git-upload-pack (git pull)
+  def git_upload_pack
+    if upload_pack? && upload_pack_allowed?
+      render_ok
+    else
+      render_not_found
+    end
+  end
+
+  # POST /foo/bar.git/git-receive-pack" (git push)
+  def git_receive_pack
+    if receive_pack? && receive_pack_allowed?
+      render_ok
+    else
+      render_not_found
+    end
+  end
+
+  private
+
+  def authenticate_user
+    return if project && project.public? && upload_pack?
+
+    authenticate_or_request_with_http_basic do |login, password|
+      auth_result = Gitlab::Auth.find_for_git_client(login, password, project: project, ip: request.ip)
+
+      if auth_result.type == :ci && upload_pack?
+        @ci = true
+      elsif auth_result.type == :oauth && !upload_pack?
+        # Not allowed
+      else
+        @user = auth_result.user
+      end
+
+      ci? || user
+    end
+  end
+
+  def ensure_project_found!
+    render_not_found if project.blank?
+  end
+
+  def project
+    return @project if defined?(@project)
+
+    project_id, _ = project_id_with_suffix
+    if project_id.blank?
+      @project = nil
+    else
+      @project = Project.find_with_namespace("#{params[:namespace_id]}/#{project_id}")
+    end
+  end
+
+  # This method returns two values so that we can parse
+  # params[:project_id] (untrusted input!) in exactly one place.
+  def project_id_with_suffix
+    id = params[:project_id] || ''
+
+    %w[.wiki.git .git].each do |suffix|
+      if id.end_with?(suffix)
+        # Be careful to only remove the suffix from the end of 'id'.
+        # Accidentally removing it from the middle is how security
+        # vulnerabilities happen!
+        return [id.slice(0, id.length - suffix.length), suffix]
+      end
+    end
+
+    # Something is wrong with params[:project_id]; do not pass it on.
+    [nil, nil]
+  end
+
+  def upload_pack?
+    git_command == 'git-upload-pack'
+  end
+
+  def receive_pack?
+    git_command == 'git-receive-pack'
+  end
+
+  def git_command
+    if action_name == 'info_refs'
+      params[:service]
+    else
+      action_name.dasherize
+    end
+  end
+
+  def render_ok
+    render json: Gitlab::Workhorse.git_http_ok(repository, user)
+  end
+
+  def repository
+    _, suffix = project_id_with_suffix
+    if suffix == '.wiki.git'
+      project.wiki.repository
+    else
+      project.repository
+    end
+  end
+
+  def render_not_found
+    render text: 'Not Found', status: :not_found
+  end
+
+  def ci?
+    @ci.present?
+  end
+
+  def upload_pack_allowed?
+    return false unless Gitlab.config.gitlab_shell.upload_pack
+
+    if user
+      Gitlab::GitAccess.new(user, project).download_access_check.allowed?
+    else
+      ci? || project.public?
+    end
+  end
+
+  def receive_pack_allowed?
+    return false unless Gitlab.config.gitlab_shell.receive_pack
+
+    # Skip user authorization on upload request.
+    # It will be done by the pre-receive hook in the repository.
+    user.present?
+  end
+end
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index 47524b1cf0b42347bcaa71f063d5f494fc428498..a60027ff4779f25bb3d36bcfd6383830a1b1e005 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -63,7 +63,8 @@ class Projects::HooksController < Projects::ApplicationController
       :push_events,
       :tag_push_events,
       :token,
-      :url
+      :url,
+      :wiki_page_events
     )
   end
 end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 8a72d691c72c9e2d65606a55706c09eca323f000..0ca0593bdc01f30280cbfd325579b8fcfbd94b83 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -1,6 +1,7 @@
 class Projects::IssuesController < Projects::ApplicationController
   include ToggleSubscriptionAction
   include IssuableActions
+  include ToggleAwardEmoji
 
   before_action :module_enabled
   before_action :issue, only: [:edit, :update, :show, :referenced_merge_requests,
@@ -68,7 +69,7 @@ class Projects::IssuesController < Projects::ApplicationController
 
   def show
     @note     = @project.notes.new(noteable: @issue)
-    @notes    = @issue.notes.nonawards.with_associations.fresh
+    @notes    = @issue.notes.with_associations.fresh
     @noteable = @issue
 
     respond_to do |format|
@@ -161,7 +162,12 @@ class Projects::IssuesController < Projects::ApplicationController
 
   def bulk_update
     result = Issues::BulkUpdateService.new(project, current_user, bulk_update_params).execute
-    redirect_back_or_default(default: { action: 'index' }, options: { notice: "#{result[:count]} issues updated" })
+
+    respond_to do |format|
+      format.json do
+        render json: { notice: "#{result[:count]} issues updated" }
+      end
+    end
   end
 
   protected
@@ -175,6 +181,7 @@ class Projects::IssuesController < Projects::ApplicationController
   end
   alias_method :subscribable_resource, :issue
   alias_method :issuable, :issue
+  alias_method :awardable, :issue
 
   def authorize_read_issue!
     return render_404 unless can?(current_user, :read_issue, @issue)
@@ -224,7 +231,10 @@ class Projects::IssuesController < Projects::ApplicationController
       :issues_ids,
       :assignee_id,
       :milestone_id,
-      :state_event
+      :state_event,
+      label_ids: [],
+      add_label_ids: [],
+      remove_label_ids: []
     )
   end
 end
diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb
index ff771ea6d9cc444e511dc5aeb637d3fec2303534..0ca675623e55d42ee72800f16a46bc3ba569db50 100644
--- a/app/controllers/projects/labels_controller.rb
+++ b/app/controllers/projects/labels_controller.rb
@@ -5,13 +5,14 @@ class Projects::LabelsController < Projects::ApplicationController
   before_action :label, only: [:edit, :update, :destroy]
   before_action :authorize_read_label!
   before_action :authorize_admin_labels!, only: [
-    :new, :create, :edit, :update, :generate, :destroy
+    :new, :create, :edit, :update, :generate, :destroy, :remove_priority, :set_priorities
   ]
 
   respond_to :js, :html
 
   def index
-    @labels = @project.labels.page(params[:page])
+    @labels = @project.labels.unprioritized.page(params[:page])
+    @prioritized_labels = @project.labels.prioritized
 
     respond_to do |format|
       format.html
@@ -71,6 +72,30 @@ class Projects::LabelsController < Projects::ApplicationController
     end
   end
 
+  def remove_priority
+    respond_to do |format|
+      if label.update_attribute(:priority, nil)
+        format.json { render json: label }
+      else
+        message = label.errors.full_messages.uniq.join('. ')
+        format.json { render json: { message: message }, status: :unprocessable_entity }
+      end
+    end
+  end
+
+  def set_priorities
+    Label.transaction do
+      params[:label_ids].each_with_index do |label_id, index|
+        label = @project.labels.find_by_id(label_id)
+        label.update_attribute(:priority, index) if label
+      end
+    end
+
+    respond_to do |format|
+      format.json { render json: { message: 'success' } }
+    end
+  end
+
   protected
 
   def module_enabled
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 9a0e6a2962b4917569426a343823bcfe4270227d..a136df2bebe9f9ecd6b8f5eee1d945f38bddc8e2 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -2,6 +2,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
   include ToggleSubscriptionAction
   include DiffHelper
   include IssuableActions
+  include ToggleAwardEmoji
 
   before_action :module_enabled
   before_action :merge_request, only: [
@@ -58,9 +59,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController
 
     respond_to do |format|
       format.html
-      format.json { render json: @merge_request, methods: :rebase_in_progress? }
-      format.diff { render text: @merge_request.to_diff }
-      format.patch { render text: @merge_request.to_patch }
+      format.json   { render json: @merge_request, methods: :rebase_in_progress? }
+      format.patch  { render text: @merge_request.to_patch }
+      format.diff do
+        return render_404 unless @merge_request.diff_refs
+
+        send_git_diff @project.repository, @merge_request.diff_refs
+      end
     end
   end
 
@@ -120,8 +125,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
     @diffs = @merge_request.compare.diffs(diff_options) if @merge_request.compare
     @diff_notes_disabled = true
 
-    @ci_commit = @merge_request.ci_commit
-    @statuses = @ci_commit.statuses if @ci_commit
+    @pipeline = @merge_request.pipeline
+    @statuses = @pipeline.statuses if @pipeline
 
     @note_counts = Note.where(commit_id: @commits.map(&:id)).
       group(:commit_id).count
@@ -129,8 +134,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
     set_suggested_approvers
   end
 
-
-
   def create
     @target_branches ||= []
     @merge_request = MergeRequests::CreateService.new(project, current_user, merge_request_params).execute
@@ -199,19 +202,34 @@ class Projects::MergeRequestsController < Projects::ApplicationController
     end
 
     merge_request_service = MergeRequests::MergeService.new(@project, current_user, merge_params)
+
     unless merge_request_service.hooks_validation_pass?(@merge_request)
       @status = :hook_validation_error
       return
     end
 
+    if params[:sha] != @merge_request.source_sha
+      @status = :sha_mismatch
+      return
+    end
+
     TodoService.new.merge_merge_request(merge_request, current_user)
 
     @merge_request.update(merge_error: nil)
 
-    if params[:merge_when_build_succeeds].present? && @merge_request.ci_commit && @merge_request.ci_commit.active?
-      MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params)
-                                                      .execute(@merge_request)
-      @status = :merge_when_build_succeeds
+    if params[:merge_when_build_succeeds].present? 
+      if @merge_request.pipeline && @merge_request.pipeline.active?
+        MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params)
+                                                        .execute(@merge_request)
+        @status = :merge_when_build_succeeds
+      elsif @merge_request.pipeline.success?
+        # This can be triggered when a user clicks the auto merge button while
+        # the tests finish at about the same time
+        MergeWorker.perform_async(@merge_request.id, current_user.id, params)
+        @status = :success
+      else
+        @status = :failed
+      end
     else
       MergeWorker.perform_async(@merge_request.id, current_user.id, params)
       @status = :success
@@ -226,7 +244,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
   end
 
   def branch_from
-    #This is always source
+    # This is always source
     @source_project = @merge_request.nil? ? @project : @merge_request.source_project
     @commit = @repository.commit(params[:ref]) if params[:ref].present?
     render layout: false
@@ -246,10 +264,12 @@ class Projects::MergeRequestsController < Projects::ApplicationController
   end
 
   def ci_status
-    ci_commit = @merge_request.ci_commit
-    if ci_commit
-      status = ci_commit.status
-      coverage = ci_commit.try(:coverage)
+    pipeline = @merge_request.pipeline
+    if pipeline
+      status = pipeline.status
+      coverage = pipeline.try(:coverage)
+
+      status ||= "preparing"
     else
       ci_service = @merge_request.source_project.ci_service
       status = ci_service.commit_status(merge_request.last_commit.sha, merge_request.source_branch) if ci_service
@@ -259,8 +279,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
       end
     end
 
-    status = "preparing" if status.nil?
-
     response = {
       title: merge_request.title,
       sha: merge_request.last_commit_short_sha,
@@ -298,6 +316,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
   end
   alias_method :subscribable_resource, :merge_request
   alias_method :issuable, :merge_request
+  alias_method :awardable, :merge_request
 
   def closes_issues
     @closes_issues ||= @merge_request.closes_issues
@@ -333,7 +352,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
   def define_show_vars
     # Build a note object for comment form
     @note = @project.notes.new(noteable: @merge_request)
-    @notes = @merge_request.mr_and_commit_notes.nonawards.inc_author.fresh
+    @notes = @merge_request.mr_and_commit_notes.inc_author.fresh
     @discussions = @notes.discussions
     @noteable = @merge_request
 
@@ -343,8 +362,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
 
     @merge_request_diff = @merge_request.merge_request_diff
 
-    @ci_commit = @merge_request.ci_commit
-    @statuses = @ci_commit.statuses if @ci_commit
+    @pipeline = @merge_request.pipeline
+    @statuses = @pipeline.statuses if @pipeline
 
     if @merge_request.locked_long_ago?
       @merge_request.unlock_mr
@@ -353,8 +372,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
   end
 
   def define_widget_vars
-    @ci_commit = @merge_request.ci_commit
-    @ci_commits = [@ci_commit].compact
+    @pipeline = @merge_request.pipeline
+    @pipelines = [@pipeline].compact
     closes_issues
   end
 
@@ -366,8 +385,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
   def set_suggested_approvers
     if @merge_request.requires_approve?
       @suggested_approvers = Gitlab::AuthorityAnalyzer.new(
-        @merge_request,
-        current_user
+        @merge_request
       ).calculate(@merge_request.approvals_required)
     end
   end
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index 40b24d550e0efe0638171c3cbd8c61f22dbb0bf7..836f79ff0803a936c7d7cc93f02cb382ceda95b8 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -1,9 +1,11 @@
 class Projects::NotesController < Projects::ApplicationController
+  include ToggleAwardEmoji
+
   # Authorize
   before_action :authorize_read_note!
   before_action :authorize_create_note!, only: [:create]
   before_action :authorize_admin_note!, only: [:update, :destroy]
-  before_action :find_current_user_notes, except: [:destroy, :delete_attachment, :award_toggle]
+  before_action :find_current_user_notes, only: [:index]
 
   def index
     current_fetched_at = Time.now.to_i
@@ -56,35 +58,12 @@ class Projects::NotesController < Projects::ApplicationController
     end
   end
 
-  def award_toggle
-    noteable = if note_params[:noteable_type] == "issue"
-                 project.issues.find(note_params[:noteable_id])
-               else
-                 project.merge_requests.find(note_params[:noteable_id])
-               end
-
-    data = {
-      author: current_user,
-      is_award: true,
-      note: note_params[:note].delete(":")
-    }
-
-    note = noteable.notes.find_by(data)
-
-    if note
-      note.destroy
-    else
-      Notes::CreateService.new(project, current_user, note_params).execute
-    end
-
-    render json: { ok: true }
-  end
-
   private
 
   def note
     @note ||= @project.notes.find(params[:id])
   end
+  alias_method :awardable, :note
 
   def note_to_html(note)
     render_to_string(
@@ -131,13 +110,20 @@ class Projects::NotesController < Projects::ApplicationController
   end
 
   def note_json(note)
-    if note.valid?
+    if note.is_a?(AwardEmoji)
+      {
+        valid:  note.valid?,
+        award:  true,
+        id:     note.id,
+        name:   note.name
+      }
+    elsif note.valid?
       {
         valid: true,
         id: note.id,
         discussion_id: note.discussion_id,
         html: note_to_html(note),
-        award: note.is_award,
+        award: false,
         note: note.note,
         discussion_html: note_to_discussion_html(note),
         discussion_with_diff_html: note_to_discussion_with_diff_html(note)
@@ -145,7 +131,7 @@ class Projects::NotesController < Projects::ApplicationController
     else
       {
         valid: false,
-        award: note.is_award,
+        award: false,
         errors: note.errors
       }
     end
diff --git a/app/controllers/projects/path_locks_controller.rb b/app/controllers/projects/path_locks_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e4d26fa525e1a782fff75336d8b7f085238e1b6f
--- /dev/null
+++ b/app/controllers/projects/path_locks_controller.rb
@@ -0,0 +1,53 @@
+class Projects::PathLocksController < Projects::ApplicationController
+  include PathLocksHelper
+
+  # Authorize
+  before_action :require_non_empty_project
+  before_action :authorize_push_code!, only: [:toggle]
+
+  before_action :check_license
+
+  def index
+    @path_locks = @project.path_locks.page(params[:page])
+  end
+
+  def toggle
+    path_lock = @project.path_locks.find_by(path: params[:path])
+
+    if path_lock
+      PathLocks::UnlockService.new(project, current_user).execute(path_lock)
+    else
+      PathLocks::LockService.new(project, current_user).execute(params[:path])
+    end
+
+    head :ok
+  rescue PathLocks::UnlockService::AccessDenied, PathLocks::LockService::AccessDenied
+    return access_denied!
+  end
+
+  def destroy
+    path_lock = @project.path_locks.find(params[:id])
+
+    begin
+      PathLocks::UnlockService.new(project, current_user).execute(path_lock)
+    rescue PathLocks::UnlockService::AccessDenied
+      return access_denied!
+    end
+
+    respond_to do |format|
+      format.html do
+        redirect_to namespace_project_locks_path(@project.namespace, @project)
+      end
+      format.js
+    end
+  end
+
+  private
+
+  def check_license
+    unless license_allows_file_locks?
+      flash[:alert] = 'You need a different license to enable FileLocks feature'
+      redirect_to admin_license_path
+    end
+  end
+end
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index b36081205d872331ce98a778775aaee637fc26cd..127bd1a43188106d17629bd8f41b3e9b1d3a4d0c 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -7,7 +7,7 @@ class Projects::PipelinesController < Projects::ApplicationController
 
   def index
     @scope = params[:scope]
-    all_pipelines = project.ci_commits
+    all_pipelines = project.pipelines
     @pipelines_count = all_pipelines.count
     @running_or_pending_count = all_pipelines.running_or_pending.count
     @pipelines = PipelinesFinder.new(project).execute(all_pipelines, @scope)
@@ -15,7 +15,7 @@ class Projects::PipelinesController < Projects::ApplicationController
   end
 
   def new
-    @pipeline = project.ci_commits.new(ref: @project.default_branch)
+    @pipeline = project.pipelines.new(ref: @project.default_branch)
   end
 
   def create
@@ -32,7 +32,7 @@ class Projects::PipelinesController < Projects::ApplicationController
   end
 
   def retry
-    pipeline.retry_failed
+    pipeline.retry_failed(current_user)
 
     redirect_back_or_default default: namespace_project_pipelines_path(project.namespace, project)
   end
@@ -50,7 +50,7 @@ class Projects::PipelinesController < Projects::ApplicationController
   end
 
   def pipeline
-    @pipeline ||= project.ci_commits.find_by!(id: params[:id])
+    @pipeline ||= project.pipelines.find_by!(id: params[:id])
   end
 
   def commit
diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb
index bb3c9d3ce58c2599b8e3ab7eae2a426e68f4b2c7..e6b5bd2999f359b9f6b8a2a1345f8ea88d681fe9 100644
--- a/app/controllers/projects/project_members_controller.rb
+++ b/app/controllers/projects/project_members_controller.rb
@@ -1,10 +1,12 @@
 class Projects::ProjectMembersController < Projects::ApplicationController
+  include MembershipActions
+
   # Authorize
-  before_action :authorize_admin_project_member!, except: [:leave, :index]
+  before_action :authorize_admin_project_member!, except: [:index, :leave, :request_access]
 
   def index
     @project_members = @project.project_members
-    @project_members = @project_members.non_invite unless can?(current_user, :admin_project, @project)
+    @project_members = @project_members.non_pending unless can?(current_user, :admin_project, @project)
 
     if params[:search].present?
       users = @project.users.search(params[:search]).to_a
@@ -14,9 +16,10 @@ class Projects::ProjectMembersController < Projects::ApplicationController
     @project_members = @project_members.order('access_level DESC')
 
     @group = @project.group
+
     if @group
       @group_members = @group.group_members
-      @group_members = @group_members.non_invite unless can?(current_user, :admin_group, @group)
+      @group_members = @group_members.non_pending unless can?(current_user, :admin_group, @group)
 
       if params[:search].present?
         users = @group.users.search(params[:search]).to_a
@@ -84,28 +87,6 @@ class Projects::ProjectMembersController < Projects::ApplicationController
     end
   end
 
-  def leave
-    @project_member = @project.project_members.find_by(user_id: current_user)
-
-    if can?(current_user, :destroy_project_member, @project_member)
-      @project_member.destroy
-
-      log_audit_event(@project_member, action: :destroy)
-
-      respond_to do |format|
-        format.html { redirect_to dashboard_projects_path, notice: "You left the project." }
-        format.js { head :ok }
-      end
-    else
-      if current_user == @project.owner
-        message = 'You can not leave your own project. Transfer or delete the project.'
-        redirect_back_or_default(default: { action: 'index' }, options: { alert: message })
-      else
-        render_403
-      end
-    end
-  end
-
   def apply_import
     source_project = Project.find(params[:source_project_id])
 
@@ -126,8 +107,10 @@ class Projects::ProjectMembersController < Projects::ApplicationController
     params.require(:project_member).permit(:user_id, :access_level)
   end
 
-  def log_audit_event(member, options = {})
-    AuditEventService.new(current_user, @project, options).
-      for_member(member).security_event
+  # MembershipActions concern
+  alias_method :membershipable, :project
+
+  def cannot_leave?
+    current_user == @project.owner
   end
 end
diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb
index 10de0e60530673a166278196a66813fb1acc5a34..10d24da16d7b5052b4572fab0cb6c9b180110421 100644
--- a/app/controllers/projects/raw_controller.rb
+++ b/app/controllers/projects/raw_controller.rb
@@ -18,10 +18,7 @@ class Projects::RawController < Projects::ApplicationController
       if @blob.lfs_pointer?
         send_lfs_object
       else
-        headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob))
-        headers['Content-Disposition'] = 'inline'
-        headers['Content-Type'] = safe_content_type(@blob)
-        head :ok # 'render nothing: true' messes up the Content-Type
+        send_git_blob @repository, @blob
       end
     else
       render_404
diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb
index d79f16e6a5abe4624cfc19ca7f405e25b7d233d2..e9a39405c2ee2694856cf2ee1d41d7d2d40b0ac9 100644
--- a/app/controllers/projects/refs_controller.rb
+++ b/app/controllers/projects/refs_controller.rb
@@ -1,6 +1,7 @@
 class Projects::RefsController < Projects::ApplicationController
   include ExtractsPath
   include TreeHelper
+  include PathLocksHelper
 
   before_action :require_non_empty_project
   before_action :validate_ref_id
@@ -57,16 +58,22 @@ class Projects::RefsController < Projects::ApplicationController
     contents.push(*tree.blobs)
     contents.push(*tree.submodules)
 
+    show_path_locks = license_allows_file_locks? && @project.path_locks.any?
+
     @logs = contents[@offset, @limit].to_a.map do |content|
       file = @path ? File.join(@path, content.name) : content.name
       last_commit = @repo.last_commit_for_path(@commit.id, file)
+      path_lock_info = show_path_locks && @project.path_lock_info(file, exact_match: true)
+
       {
         file_name: content.name,
-        commit: last_commit
+        commit: last_commit,
+        path_lock_info: path_lock_info
       }
     end
 
-    offset = (@offset + @limit)
+    offset = @offset + @limit
+
     if contents.size > offset
       @more_log_url = logs_file_namespace_project_ref_path(@project.namespace, @project, @ref, @path || '', offset: offset)
     end
diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb
index bb7a6b6a5ab76faefbbb91de60eaf6eded6799b0..d5af0341d18fda13ee340f78d1b840ffa6d53186 100644
--- a/app/controllers/projects/repositories_controller.rb
+++ b/app/controllers/projects/repositories_controller.rb
@@ -11,8 +11,7 @@ class Projects::RepositoriesController < Projects::ApplicationController
   end
 
   def archive
-    headers.store(*Gitlab::Workhorse.send_git_archive(@project, params[:ref], params[:format]))
-    head :ok
+    send_git_archive @repository, ref: params[:ref], format: params[:format]
   rescue => ex
     logger.error("#{self.class.name}: #{ex}")
     return git_not_found!
diff --git a/app/controllers/projects/todos_controller.rb b/app/controllers/projects/todos_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a51bd5e2b499ff42a7c5c205a4bd212d7d4a4248
--- /dev/null
+++ b/app/controllers/projects/todos_controller.rb
@@ -0,0 +1,31 @@
+class Projects::TodosController < Projects::ApplicationController
+  def create
+    todos = TodoService.new.mark_todo(issuable, current_user)
+
+    render json: {
+      todo: todos,
+      count: current_user.todos.pending.count,
+    }
+  end
+
+  def update
+    current_user.todos.find_by_id(params[:id]).update(state: :done)
+
+    render json: {
+      count: current_user.todos.pending.count,
+    }
+  end
+
+  private
+
+  def issuable
+    @issuable ||= begin
+      case params[:issuable_type]
+      when "issue"
+        @project.issues.find(params[:issuable_id])
+      when "merge_request"
+        @project.merge_requests.find(params[:issuable_id])
+      end
+    end
+  end
+end
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index 218bd43ce8593ba45a2ccc53bc25e5e30a25247e..3f38179fd997edb08310d670ce52cf50e86ee332 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -16,6 +16,9 @@ class Projects::WikisController < Projects::ApplicationController
     if @page
       render 'show'
     elsif file = @project_wiki.find_file(params[:id], params[:version_id])
+      response.headers['Content-Security-Policy'] = "default-src 'none'"
+      response.headers['X-Content-Security-Policy'] = "default-src 'none'"
+
       if file.on_disk?
         send_file file.on_disk_path, disposition: 'inline'
       else
@@ -100,11 +103,11 @@ class Projects::WikisController < Projects::ApplicationController
   def markdown_preview
     text = params[:text]
 
-    ext = Gitlab::ReferenceExtractor.new(@project, current_user, current_user)
-    ext.analyze(text)
+    ext = Gitlab::ReferenceExtractor.new(@project, current_user)
+    ext.analyze(text, author: current_user)
 
     render json: {
-      body: view_context.markdown(text, pipeline: :wiki, project_wiki: @project_wiki),
+      body: view_context.markdown(text, pipeline: :wiki, project_wiki: @project_wiki, page_slug: params[:id]),
       references: {
         users: ext.users.map(&:username)
       }
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index d8145a87abdea44f63ada3167e38e8fa1ffdc2e9..17d157f65b9735d32248be87ba545a1823e032ab 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -140,7 +140,7 @@ class ProjectsController < Projects::ApplicationController
     participants = ::Projects::ParticipantsService.new(@project, current_user).execute(note_type, note_id)
 
     @suggestions = {
-      emojis: AwardEmoji.urls,
+      emojis: Gitlab::AwardEmoji.urls,
       issues: autocomplete.issues,
       milestones: autocomplete.milestones,
       mergerequests: autocomplete.merge_requests,
@@ -198,8 +198,8 @@ class ProjectsController < Projects::ApplicationController
   def markdown_preview
     text = params[:text]
 
-    ext = Gitlab::ReferenceExtractor.new(@project, current_user, current_user)
-    ext.analyze(text)
+    ext = Gitlab::ReferenceExtractor.new(@project, current_user)
+    ext.analyze(text, author: current_user)
 
     render json: {
       body:       view_context.markdown(text),
@@ -235,7 +235,7 @@ class ProjectsController < Projects::ApplicationController
       :issues_tracker_id, :default_branch,
       :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar,
       :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex,
-      :public_builds,
+      :public_builds, :only_allow_merge_if_build_succeeds,
 
       # EE-only
       :approvals_before_merge,
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 546e4a11e0dfa19c20cdc511a3598e925c08fde6..ea56a4fb88b7dde3253cb1b4b568b822cd2b4ec7 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -1,5 +1,6 @@
 class SessionsController < Devise::SessionsController
   include AuthenticatesWithTwoFactor
+  include Devise::Controllers::Rememberable
   include Recaptcha::ClientHelper
 
   skip_before_action :check_2fa_requirement, only: [:destroy]
@@ -14,6 +15,7 @@ class SessionsController < Devise::SessionsController
   before_action :load_recaptcha
 
   def new
+    set_minimum_password_length
     if Gitlab.config.ldap.enabled
       @ldap_servers = Gitlab::LDAP::Config.servers
     else
@@ -30,8 +32,7 @@ class SessionsController < Devise::SessionsController
         resource.update_attributes(reset_password_token: nil,
                                    reset_password_sent_at: nil)
       end
-      authenticated_with = user_params[:otp_attempt] ? "two-factor" : "standard"
-      log_audit_event(current_user, with: authenticated_with)
+      log_audit_event(current_user, with: authentication_method)
     end
   end
 
@@ -40,7 +41,7 @@ class SessionsController < Devise::SessionsController
   # Handle an "initial setup" state, where there's only one user, it's an admin,
   # and they require a password change.
   def check_initial_setup
-    return unless User.count == 1
+    return unless User.limit(2).count == 1 # Count as much 2 to know if we have exactly one
 
     user = User.admins.last
 
@@ -54,7 +55,7 @@ class SessionsController < Devise::SessionsController
   end
 
   def user_params
-    params.require(:user).permit(:login, :password, :remember_me, :otp_attempt)
+    params.require(:user).permit(:login, :password, :remember_me, :otp_attempt, :device_response)
   end
 
   def find_user
@@ -91,26 +92,6 @@ class SessionsController < Devise::SessionsController
     find_user.try(:two_factor_enabled?)
   end
 
-  def authenticate_with_two_factor
-    user = self.resource = find_user
-
-    if user_params[:otp_attempt].present? && session[:otp_user_id]
-      if valid_otp_attempt?(user)
-        # Remove any lingering user data from login
-        session.delete(:otp_user_id)
-
-        sign_in(user) and return
-      else
-        flash.now[:alert] = 'Invalid two-factor code.'
-        render :two_factor and return
-      end
-    else
-      if user && user.valid_password?(user_params[:password])
-        prompt_for_two_factor(user)
-      end
-    end
-  end
-
   def gitlab_geo_login
     return unless Gitlab::Geo.secondary?
     return if signed_in?
@@ -159,4 +140,14 @@ class SessionsController < Devise::SessionsController
   def load_recaptcha
     Gitlab::Recaptcha.load_configurations!
   end
+
+  def authentication_method
+    if user_params[:otp_attempt]
+      "two-factor"
+    elsif user_params[:device_response]
+      "two-factor-via-u2f-device"
+    else
+      "standard"
+    end
+  end
 end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 799421c185b3bf98389f17f5aeb657c867c66d0b..a99632454d94f354a92b0b607c127b9734ba9158 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -74,8 +74,6 @@ class UsersController < ApplicationController
   def calendar
     calendar = contributions_calendar
     @timestamps = calendar.timestamps
-    @starting_year = calendar.starting_year
-    @starting_month = calendar.starting_month
 
     render 'calendar', layout: false
   end
diff --git a/app/finders/group_projects_finder.rb b/app/finders/group_projects_finder.rb
index 3b9a421b11871ff7b174918cf812778a5f184524..aa8f4c1d0e4da52daea174402259d1a7d40ff474 100644
--- a/app/finders/group_projects_finder.rb
+++ b/app/finders/group_projects_finder.rb
@@ -18,7 +18,7 @@ class GroupProjectsFinder < UnionFinder
     projects = []
 
     if current_user
-      if @group.users.include?(current_user)
+      if @group.users.include?(current_user) || current_user.admin?
         projects << @group.projects unless only_shared
         projects << @group.shared_projects unless only_owned
       else
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 4fdaf2dcb64e5c45161f10721015e99b3c1cf86a..118783f54edb64d8b437925c69cb95b2bd32190a 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -225,7 +225,7 @@ class IssuableFinder
   def sort(items)
     # Ensure we always have an explicit sort order (instead of inheriting
     # multiple orders when combining ActiveRecord::Relation objects).
-    params[:sort] ? items.sort(params[:sort]) : items.reorder(id: :desc)
+    params[:sort] ? items.sort(params[:sort], excluded_labels: label_names) : items.reorder(id: :desc)
   end
 
   def by_assignee(items)
@@ -251,12 +251,12 @@ class IssuableFinder
   def by_milestone(items)
     if milestones?
       if filter_by_no_milestone?
-        items = items.where(milestone_id: [-1, nil])
+        items = items.left_joins_milestones.where(milestone_id: [-1, nil])
       elsif filter_by_upcoming_milestone?
         upcoming_ids = Milestone.upcoming_ids_by_projects(projects)
-        items = items.joins(:milestone).where(milestone_id: upcoming_ids)
+        items = items.left_joins_milestones.where(milestone_id: upcoming_ids)
       else
-        items = items.joins(:milestone).where(milestones: { title: params[:milestone_title] })
+        items = items.with_milestone(params[:milestone_title])
 
         if projects
           items = items.where(milestones: { project_id: projects })
@@ -272,7 +272,7 @@ class IssuableFinder
       if filter_by_no_label?
         items = items.without_label
       else
-        items = items.with_label(label_names)
+        items = items.with_label(label_names, params[:sort])
         if projects
           items = items.where(labels: { project_id: projects })
         end
@@ -295,7 +295,8 @@ class IssuableFinder
   end
 
   def weights?
-    params[:weight].present? && params[:weight] != Issue::WEIGHT_ALL
+    params[:weight].present? && params[:weight] != Issue::WEIGHT_ALL &&
+      klass.column_names.include?('weight')
   end
 
   def filter_by_no_weight?
@@ -343,7 +344,11 @@ class IssuableFinder
   end
 
   def label_names
-    params[:label_name].is_a?(String) ? params[:label_name].split(',') : params[:label_name]
+    if labels?
+      params[:label_name].is_a?(String) ? params[:label_name].split(',') : params[:label_name]
+    else
+      []
+    end
   end
 
   def current_user_related?
diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb
index c41be333537f875888989a01f5db10be45d19c02..0b7832e658308fbfde55a9ec421850120e1e4c0b 100644
--- a/app/finders/notes_finder.rb
+++ b/app/finders/notes_finder.rb
@@ -12,9 +12,9 @@ class NotesFinder
       when "commit"
         project.notes.for_commit_id(target_id).non_diff_notes
       when "issue"
-        project.issues.find(target_id).notes.nonawards.inc_author
+        project.issues.visible_to_user(current_user).find(target_id).notes.inc_author
       when "merge_request"
-        project.merge_requests.find(target_id).mr_and_commit_notes.nonawards.inc_author
+        project.merge_requests.find(target_id).mr_and_commit_notes.inc_author
       when "snippet", "project_snippet"
         project.snippets.find(target_id).notes
       else
diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb
index 01cbf91c658b8a9a5d7a2d2652dce5eba14925ef..00ff161103932ede759303894fecd0cbda80d564 100644
--- a/app/finders/snippets_finder.rb
+++ b/app/finders/snippets_finder.rb
@@ -51,7 +51,7 @@ class SnippetsFinder
     snippets = project.snippets.fresh
 
     if current_user
-      if project.team.member?(current_user.id) || current_user.admin?
+      if project.team.member?(current_user) || current_user.admin?
         snippets
       else
         snippets.public_and_internal
diff --git a/app/finders/todos_finder.rb b/app/finders/todos_finder.rb
index 4bd46a76087470ceef636bf0594f3eb469b5b295..aa47c6c157e03d77c60370639531403d21a0f70e 100644
--- a/app/finders/todos_finder.rb
+++ b/app/finders/todos_finder.rb
@@ -30,13 +30,13 @@ class TodosFinder
     items = by_state(items)
     items = by_type(items)
 
-    items
+    items.reorder(id: :desc)
   end
 
   private
 
   def action_id?
-    action_id.present? && [Todo::ASSIGNED, Todo::MENTIONED, Todo::BUILD_FAILED].include?(action_id.to_i)
+    action_id.present? && [Todo::ASSIGNED, Todo::MENTIONED, Todo::BUILD_FAILED, Todo::MARKED].include?(action_id.to_i)
   end
 
   def action_id
@@ -78,6 +78,16 @@ class TodosFinder
     @project
   end
 
+  def projects
+    return @projects if defined?(@projects)
+
+    if project?
+      @projects = project
+    else
+      @projects = ProjectsFinder.new.execute(current_user)
+    end
+  end
+
   def type?
     type.present? && ['Issue', 'MergeRequest'].include?(type)
   end
@@ -105,6 +115,8 @@ class TodosFinder
   def by_project(items)
     if project?
       items = items.where(project: project)
+    elsif projects
+      items = items.merge(projects).joins(:project)
     end
 
     items
diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb
index d7e4d7c447ada6296a0b53ee3c025b2ecb079a4b..99bdbab0f9dd49a68556751478a23add99874e04 100644
--- a/app/helpers/appearances_helper.rb
+++ b/app/helpers/appearances_helper.rb
@@ -30,4 +30,8 @@ module AppearancesHelper
       render 'shared/logo.svg'
     end
   end
+
+  def navbar_icon(icon_name)
+    render "shared/icons/#{icon_name}.svg"
+  end
 end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 517faa6ca51c49f2f6e8140a244b5d88e447ef8c..39c8bb109e2cff76cf336ab31054e36efab68ca5 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -110,8 +110,7 @@ module ApplicationHelper
     ]
 
     # If reference is commit id - we should add it to branch/tag selectbox
-    if(@ref && !options.flatten.include?(@ref) &&
-       @ref =~ /\A[0-9a-zA-Z]{6,52}\z/)
+    if @ref && !options.flatten.include?(@ref) && @ref =~ /\A[0-9a-zA-Z]{6,52}\z/
       options << ['Commit', [@ref]]
     end
 
@@ -266,6 +265,8 @@ module ApplicationHelper
       assignee_id: params[:assignee_id],
       author_id: params[:author_id],
       sort: params[:sort],
+      issue_search: params[:issue_search],
+      label_name: params[:label_name]
     }
 
     options = exist_opts.merge(options)
@@ -276,16 +277,11 @@ module ApplicationHelper
       end
     end
 
-    path = request.path
-    path << "?#{options.to_param}"
-    if add_label
-      if params[:label_name].present? and params[:label_name].respond_to?('any?')
-        params[:label_name].each do |label|
-          path << "&label_name[]=#{label}"
-        end
-      end
-    end
-    path
+    params = options.compact
+
+    params.delete(:label_name) unless add_label
+
+    "#{request.path}?#{params.to_param}"
   end
 
   def outdated_browser?
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 899d4cd68fd3825b64d34f6fe9af91cc38c57999..ad086925fa4b9744ff6e0ca811ab9469884fbd13 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -18,6 +18,10 @@ module ApplicationSettingsHelper
   def help_text
     current_application_settings.help_text
   end
+  
+  def after_sign_up_text
+    current_application_settings.after_sign_up_text
+  end
 
   def shared_runners_text
     current_application_settings.shared_runners_text
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
index db7ad4ba98cda8a69fcd24408726a7fef1474d67..d5b817518af6a57859dbf79a5f16d5cd4daf236f 100644
--- a/app/helpers/auth_helper.rb
+++ b/app/helpers/auth_helper.rb
@@ -70,7 +70,7 @@ module AuthHelper
 
   def two_factor_skippable?
     current_application_settings.require_two_factor_authentication &&
-      !current_user.two_factor_enabled &&
+      !current_user.two_factor_enabled? &&
       current_application_settings.two_factor_grace_period &&
       !two_factor_grace_period_expired?
   end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 93241b3afb7762caee9157a13a9ff7667ead1f01..b134e922b5ae71943b769181c9a59cb545d89c23 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -29,7 +29,7 @@ module BlobHelper
     if !on_top_of_branch?(project, ref)
       button_tag "Edit", class: "btn disabled has-tooltip btn-file-option", title: "You can only edit files when you are on a branch", data: { container: 'body' }
     elsif can_edit_blob?(blob, project, ref)
-      link_to "Edit", edit_path, class: 'btn btn-file-option'
+      link_to "Edit", edit_path, class: 'btn btn-sm'
     elsif can?(current_user, :fork_project, project)
       continue_params = {
         to:     edit_path,
@@ -116,7 +116,7 @@ module BlobHelper
   end
 
   def blob_text_viewable?(blob)
-    blob && blob.text? && !blob.lfs_pointer?
+    blob && blob.text? && !blob.lfs_pointer? && !blob.only_display_raw?
   end
 
   def blob_size(blob)
@@ -184,4 +184,14 @@ module BlobHelper
       Other: licenses.reject(&:featured).map { |license| [license.name, license.key] }
     }
   end
+
+  def gitignore_names
+    return @gitignore_names if defined?(@gitignore_names)
+
+    @gitignore_names = {
+      Global: Gitlab::Gitignore.global.map { |gitignore| { name: gitignore.name } },
+      # Note that the key here doesn't cover it really
+      Languages: Gitlab::Gitignore.languages_frameworks.map{ |gitignore| { name: gitignore.name } }
+    }
+  end
 end
diff --git a/app/helpers/branches_helper.rb b/app/helpers/branches_helper.rb
index e39548e17e1ca0a64288ba0e8b6016d687c1f76b..3ee3fc74f0c4efe698df739d9836c841aee0dffd 100644
--- a/app/helpers/branches_helper.rb
+++ b/app/helpers/branches_helper.rb
@@ -14,4 +14,8 @@ module BranchesHelper
 
     ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name)
   end
+
+  def project_branches
+    options_for_select(@project.repository.branch_names, @project.default_branch)
+  end
 end
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index b319a8700b0ed6d1e225501744d6ea58cc8000bb..a157686c247d1c590a527674ca6636d6bdfe7ad0 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -17,7 +17,15 @@ module ButtonHelper
   def clipboard_button(data = {})
     content_tag :button,
       icon('clipboard'),
-      class: 'btn btn-clipboard',
+      class: "btn",
+      data: data,
+      type: :button
+  end
+
+  def clipboard_button_with_class(data = {}, css_class: 'btn-clipboard')
+    content_tag :button,
+      icon('clipboard'),
+      class: "btn #{css_class}",
       data: data,
       type: :button
   end
@@ -30,7 +38,7 @@ module ButtonHelper
 
     content_tag :a, protocol,
       class: klass,
-      href: @project.http_url_to_repo,
+      href: project.http_url_to_repo,
       data: {
         html: true,
         placement: 'right',
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
index cfad17dcacf598adc543574212ce7cf805d40384..8e4ae1e6aec16c1b0ff1d1d15747b3bd06da6065 100644
--- a/app/helpers/ci_status_helper.rb
+++ b/app/helpers/ci_status_helper.rb
@@ -1,7 +1,7 @@
 module CiStatusHelper
-  def ci_status_path(ci_commit)
-    project = ci_commit.project
-    builds_namespace_project_commit_path(project.namespace, project, ci_commit.sha)
+  def ci_status_path(pipeline)
+    project = pipeline.project
+    builds_namespace_project_commit_path(project.namespace, project, pipeline.sha)
   end
 
   def ci_status_with_icon(status, target = nil)
@@ -38,10 +38,10 @@ module CiStatusHelper
     icon(icon_name + ' fw')
   end
 
-  def render_commit_status(commit, tooltip_placement: 'auto left')
+  def render_commit_status(commit, tooltip_placement: 'auto left', cssclass: '')
     project = commit.project
     path = builds_namespace_project_commit_path(project.namespace, project, commit)
-    render_status_with_link('commit', commit.status, path, tooltip_placement)
+    render_status_with_link('commit', commit.status, path, tooltip_placement, cssclass: cssclass)
   end
 
   def render_pipeline_status(pipeline, tooltip_placement: 'auto left')
@@ -57,10 +57,10 @@ module CiStatusHelper
 
   private
 
-  def render_status_with_link(type, status, path, tooltip_placement)
+  def render_status_with_link(type, status, path, tooltip_placement, cssclass: '')
     link_to ci_icon_for_status(status),
             path,
-            class: "ci-status-link ci-status-icon-#{status.dasherize}",
+            class: "ci-status-link ci-status-icon-#{status.dasherize} #{cssclass}",
             title: "#{type.titleize}: #{ci_label_for_status(status)}",
             data: { toggle: 'tooltip', placement: tooltip_placement }
   end
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index b59c3982edd169c07b53de27e1228822e82f24f7..474041eccbb60ca7ccc7f683acbb9e2e4234d040 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -16,6 +16,16 @@ module CommitsHelper
     commit_person_link(commit, options.merge(source: :committer))
   end
 
+  def commit_author_avatar(commit, options = {})
+    options = options.merge(source: :author)
+    user = commit.send(options[:source])
+
+    source_email = clean(commit.send "#{options[:source]}_email".to_sym)
+    person_email = user.try(:email) || source_email
+
+    image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]} hidden-xs", width: options[:size], alt: "")
+  end
+
   def image_diff_class(diff)
     if diff.deleted_file
       "deleted"
@@ -102,34 +112,35 @@ module CommitsHelper
     if current_controller?(:projects, :commits)
       if @repo.blob_at(commit.id, @path)
         return link_to(
-          "Browse File »",
+          "Browse File",
           namespace_project_blob_path(project.namespace, project,
                                       tree_join(commit.id, @path)),
-          class: "pull-right"
+          class: "btn btn-default"
         )
       elsif @path.present?
         return link_to(
-          "Browse Directory »",
+          "Browse Directory",
           namespace_project_tree_path(project.namespace, project,
                                       tree_join(commit.id, @path)),
-          class: "pull-right"
+          class: "btn btn-default"
         )
       end
     end
     link_to(
       "Browse Files",
       namespace_project_tree_path(project.namespace, project, commit),
-      class: "pull-right"
+      class: "btn btn-default"
     )
   end
 
-  def revert_commit_link(commit, continue_to_path, btn_class: nil)
+  def revert_commit_link(commit, continue_to_path, btn_class: nil, has_tooltip: true)
     return unless current_user
 
-    tooltip = "Revert this #{commit.change_type_title} in a new merge request"
+    tooltip = "Revert this #{commit.change_type_title} in a new merge request" if has_tooltip
 
     if can_collaborate_with_project?
-      link_to 'Revert', '#modal-revert-commit', 'data-toggle' => 'modal', 'data-container' => 'body', title: tooltip, class: "btn btn-default btn-grouped btn-#{btn_class} has-tooltip"
+      btn_class = "btn btn-warning btn-#{btn_class}" unless btn_class.nil?
+      link_to 'Revert', '#modal-revert-commit', 'data-toggle' => 'modal', 'data-container' => 'body', title: (tooltip if has_tooltip), class: "#{btn_class} #{'has-tooltip' if has_tooltip}"
     elsif can?(current_user, :fork_project, @project)
       continue_params = {
         to: continue_to_path,
@@ -140,17 +151,20 @@ module CommitsHelper
         namespace_key: current_user.namespace.id,
         continue: continue_params)
 
-      link_to 'Revert', fork_path, class: 'btn btn-grouped btn-close', method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: tooltip
+      btn_class = "btn btn-grouped btn-warning" unless btn_class.nil?
+
+      link_to 'Revert', fork_path, class: btn_class, method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: (tooltip if has_tooltip)
     end
   end
 
-  def cherry_pick_commit_link(commit, continue_to_path, btn_class: nil)
+  def cherry_pick_commit_link(commit, continue_to_path, btn_class: nil, has_tooltip: true)
     return unless current_user
 
     tooltip = "Cherry-pick this #{commit.change_type_title} in a new merge request"
 
     if can_collaborate_with_project?
-      link_to 'Cherry-pick', '#modal-cherry-pick-commit', 'data-toggle' => 'modal', 'data-container' => 'body', title: tooltip, class: "btn btn-default btn-grouped btn-#{btn_class} has-tooltip"
+      btn_class = "btn btn-default btn-#{btn_class}" unless btn_class.nil?
+      link_to 'Cherry-pick', '#modal-cherry-pick-commit', 'data-toggle' => 'modal', 'data-container' => 'body', title: (tooltip if has_tooltip), class: "#{btn_class} #{'has-tooltip' if has_tooltip}"
     elsif can?(current_user, :fork_project, @project)
       continue_params = {
         to: continue_to_path,
@@ -161,7 +175,8 @@ module CommitsHelper
         namespace_key: current_user.namespace.id,
         continue: continue_params)
 
-      link_to 'Cherry-pick', fork_path, class: 'btn btn-grouped btn-close', method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: tooltip
+      btn_class = "btn btn-grouped btn-close" unless btn_class.nil?
+      link_to 'Cherry-pick', fork_path, class: "#{btn_class}", method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: (tooltip if has_tooltip)
     end
   end
 
@@ -182,12 +197,10 @@ module CommitsHelper
     source_email = clean(commit.send "#{options[:source]}_email".to_sym)
 
     person_name = user.try(:name) || source_name
-    person_email = user.try(:email) || source_email
 
     text =
       if options[:avatar]
-        avatar = image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "")
-        %Q{#{avatar} <span class="commit-#{options[:source]}-name">#{person_name}</span>}
+        %Q{<span class="commit-#{options[:source]}-name">#{person_name}</span>}
       else
         person_name
       end
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index ea383f9b0f6fe68cb709b909d5f33b7d50b23c48..e22dce59d0fb8afd423e141a08dbfeef58658378 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -39,11 +39,11 @@ module DiffHelper
   end
 
   def unfold_bottom_class(bottom)
-    (bottom) ? 'js-unfold-bottom' : ''
+    bottom ? 'js-unfold-bottom' : ''
   end
 
   def unfold_class(unfold)
-    (unfold) ? 'unfold js-unfold' : ''
+    unfold ? 'unfold js-unfold' : ''
   end
 
   def diff_line_content(line, line_type = nil)
@@ -135,6 +135,11 @@ module DiffHelper
     toggle_whitespace_link(url, options)
   end
 
+  def diff_compare_whitespace_link(project, from, to, options)
+    url = namespace_project_compare_path(project.namespace, project, from, to, params_with_whitespace)
+    toggle_whitespace_link(url, options)
+  end
+
   private
 
   def hide_whitespace?
diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb
index 14697f774ccc90acb5a79e103495f8d03536ad30..6b617e1730a627be19dcfd7adddc94ac81a0a821 100644
--- a/app/helpers/dropdowns_helper.rb
+++ b/app/helpers/dropdowns_helper.rb
@@ -67,9 +67,9 @@ module DropdownsHelper
     end
   end
 
-  def dropdown_filter(placeholder)
+  def dropdown_filter(placeholder, search_id: nil)
     content_tag :div, class: "dropdown-input" do
-      filter_output = search_field_tag nil, nil, class: "dropdown-input-field", placeholder: placeholder
+      filter_output = search_field_tag search_id, nil, class: "dropdown-input-field", placeholder: placeholder
       filter_output << icon('search', class: "dropdown-input-search")
       filter_output << icon('times', class: "dropdown-input-clear js-dropdown-input-clear", role: "button")
 
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index e14893817065f5640e5dcd866e3581fb4642287a..bfedcb1c42b62714b6ac6945589f721a10fed85d 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -159,28 +159,6 @@ module EventsHelper
     "--broken encoding"
   end
 
-  def event_to_atom(xml, event)
-    if event.visible_to_user?(current_user)
-      xml.entry do
-        event_link = event_feed_url(event)
-        event_title = event_feed_title(event)
-        event_summary = event_feed_summary(event)
-
-        xml.id      "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"
-        xml.link    href: event_link
-        xml.title   truncate(event_title, length: 80)
-        xml.updated event.created_at.xmlschema
-        xml.media   :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(event.author_email))
-        xml.author do |author|
-          xml.name event.author_name
-          xml.email event.author_email
-        end
-
-        xml.summary(type: "xhtml") { |x| x << event_summary unless event_summary.nil? }
-      end
-    end
-  end
-
   def event_row_class(event)
     if event.body?
       "event-block"
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index 3a45205563e72181831f6e8efe0e3381d301c519..067a00660aaa6560128d6b1bdaa0156b761c03bd 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -13,7 +13,7 @@ module GitlabMarkdownHelper
   def link_to_gfm(body, url, html_options = {})
     return "" if body.blank?
 
-    escaped_body = if body =~ /\A\<img/
+    escaped_body = if body.start_with?('<img')
                      body
                    else
                      escape_once(body)
@@ -108,7 +108,7 @@ module GitlabMarkdownHelper
   def render_wiki_content(wiki_page)
     case wiki_page.format
     when :markdown
-      markdown(wiki_page.content, pipeline: :wiki, project_wiki: @project_wiki)
+      markdown(wiki_page.content, pipeline: :wiki, project_wiki: @project_wiki, page_slug: wiki_page.slug)
     when :asciidoc
       asciidoc(wiki_page.content)
     else
diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb
index 2ce2d4e694f38cae75a009d7ec2aba16e9c2958b..5386ddadc62bfe7efd8f20ad2a17f089fcdd8639 100644
--- a/app/helpers/gitlab_routing_helper.rb
+++ b/app/helpers/gitlab_routing_helper.rb
@@ -13,10 +13,23 @@
 #   merge_request_path(merge_request)
 #
 module GitlabRoutingHelper
+  # Project
   def project_path(project, *args)
     namespace_project_path(project.namespace, project, *args)
   end
 
+  def project_url(project, *args)
+    namespace_project_url(project.namespace, project, *args)
+  end
+
+  def edit_project_path(project, *args)
+    edit_namespace_project_path(project.namespace, project, *args)
+  end
+
+  def edit_project_url(project, *args)
+    edit_namespace_project_url(project.namespace, project, *args)
+  end
+
   def project_files_path(project, *args)
     namespace_project_tree_path(project.namespace, project, @ref || project.repository.root_ref)
   end
@@ -29,6 +42,10 @@ module GitlabRoutingHelper
     namespace_project_pipelines_path(project.namespace, project, *args)
   end
 
+  def project_environments_path(project, *args)
+    namespace_project_environments_path(project.namespace, project, *args)
+  end
+
   def project_builds_path(project, *args)
     namespace_project_builds_path(project.namespace, project, *args)
   end
@@ -41,10 +58,6 @@ module GitlabRoutingHelper
     activity_namespace_project_path(project.namespace, project, *args)
   end
 
-  def edit_project_path(project, *args)
-    edit_namespace_project_path(project.namespace, project, *args)
-  end
-
   def runners_path(project, *args)
     namespace_project_runners_path(project.namespace, project, *args)
   end
@@ -65,14 +78,6 @@ module GitlabRoutingHelper
     namespace_project_milestone_path(entity.project.namespace, entity.project, entity, *args)
   end
 
-  def project_url(project, *args)
-    namespace_project_url(project.namespace, project, *args)
-  end
-
-  def edit_project_url(project, *args)
-    edit_namespace_project_url(project.namespace, project, *args)
-  end
-
   def issue_url(entity, *args)
     namespace_project_issue_url(entity.project.namespace, entity.project, entity, *args)
   end
@@ -92,4 +97,56 @@ module GitlabRoutingHelper
       toggle_subscription_namespace_project_merge_request_path(entity.project.namespace, entity.project, entity)
     end
   end
+
+  ## Members
+  def project_members_url(project, *args)
+    namespace_project_project_members_url(project.namespace, project)
+  end
+
+  def project_member_path(project_member, *args)
+    namespace_project_project_member_path(project_member.source.namespace, project_member.source, project_member)
+  end
+
+  def request_access_project_members_path(project, *args)
+    request_access_namespace_project_project_members_path(project.namespace, project)
+  end
+
+  def leave_project_members_path(project, *args)
+    leave_namespace_project_project_members_path(project.namespace, project)
+  end
+
+  def approve_access_request_project_member_path(project_member, *args)
+    approve_access_request_namespace_project_project_member_path(project_member.source.namespace, project_member.source, project_member)
+  end
+
+  def resend_invite_project_member_path(project_member, *args)
+    resend_invite_namespace_project_project_member_path(project_member.source.namespace, project_member.source, project_member)
+  end
+
+  # Groups
+
+  ## Members
+  def group_members_url(group, *args)
+    group_group_members_url(group, *args)
+  end
+
+  def group_member_path(group_member, *args)
+    group_group_member_path(group_member.source, group_member)
+  end
+
+  def request_access_group_members_path(group, *args)
+    request_access_group_group_members_path(group)
+  end
+
+  def leave_group_members_path(group, *args)
+    leave_group_group_members_path(group)
+  end
+
+  def approve_access_request_group_member_path(group_member, *args)
+    approve_access_request_group_group_member_path(group_member.source, group_member)
+  end
+
+  def resend_invite_group_member_path(group_member, *args)
+    resend_invite_group_group_member_path(group_member.source, group_member)
+  end
 end
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 6a0b89c75828ca1dd732b789e7d4ca8b7c3e3abb..b9211e884733f693e0905a3fbcc2c748564a244f 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -1,24 +1,4 @@
 module GroupsHelper
-  def remove_user_from_group_message(group, member)
-    if member.user
-      "Are you sure you want to remove \"#{member.user.name}\" from \"#{group.name}\"?"
-    else
-      "Are you sure you want to revoke the invitation for \"#{member.invite_email}\" to join \"#{group.name}\"?"
-    end
-  end
-
-  def leave_group_message(group)
-    "Are you sure you want to leave \"#{group}\" group?"
-  end
-
-  def should_user_see_group_roles?(user, group)
-    if user && group
-      user.is_admin? || group.members.exists?(user_id: user.id)
-    else
-      false
-    end
-  end
-
   def can_change_group_visibility_level?(group)
     can?(current_user, :change_visibility_level, group)
   end
@@ -31,7 +11,7 @@ module GroupsHelper
     if group && group.avatar.present?
       group.avatar.url
     else
-      'no_group_avatar.png'
+      image_path('no_group_avatar.png')
     end
   end
 
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 3947421728604a976bc8c0dbb91ee13b641ea77f..8dbc51a689f303e618f0773e9f22a2d634e71465 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -8,14 +8,6 @@ module IssuablesHelper
     "right-sidebar-#{sidebar_gutter_collapsed? ? 'collapsed' : 'expanded'}"
   end
 
-  def issuables_count(issuable)
-    base_issuable_scope(issuable).maximum(:iid)
-  end
-
-  def next_issuable_for(issuable)
-    base_issuable_scope(issuable).where('iid > ?', issuable.iid).last
-  end
-
   def multi_label_name(current_labels, default_label)
     # current_labels may be a string from before
     if current_labels.is_a?(Array)
@@ -45,19 +37,11 @@ module IssuablesHelper
     end
   end
 
-  def prev_issuable_for(issuable)
-    base_issuable_scope(issuable).where('iid < ?', issuable.iid).first
-  end
-
   def user_dropdown_label(user_id, default_label)
+    return default_label if user_id.nil?
     return "Unassigned" if user_id == "0"
 
-    if @project
-      member = @project.team.find_member(user_id)
-      user = member.user if member
-    else
-      user = User.find_by(id: user_id)
-    end
+    user = User.find_by(id: user_id)
 
     if user
       user.name
@@ -76,13 +60,19 @@ module IssuablesHelper
 
   def issuable_meta(issuable, project, text)
     output = content_tag :strong, "#{text} #{issuable.to_reference}", class: "identifier"
-    output << " opened #{time_ago_with_tooltip(issuable.created_at)} by".html_safe
+    output << " opened #{time_ago_with_tooltip(issuable.created_at)} by ".html_safe
     output << content_tag(:strong) do
       author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs")
       author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "hidden-sm hidden-md hidden-lg")
     end
   end
 
+  def has_todo(issuable)
+    unless current_user.nil?
+      current_user.todos.find_by(target_id: issuable.id, state: :pending)
+    end
+  end
+
   private
 
   def sidebar_gutter_collapsed?
@@ -100,5 +90,4 @@ module IssuablesHelper
       issuable.open? ? :opened : :closed
     end
   end
-
 end
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index e24588be7ebb6fde75f91b7c024bf38118ed76f4..e85f5358a860151897aa041f3d57b6c33e713d98 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -105,23 +105,6 @@ module IssuesHelper
     return 'hidden' if issue.closed? == closed
   end
 
-  def issue_to_atom(xml, issue)
-    xml.entry do
-      xml.id      namespace_project_issue_url(issue.project.namespace,
-                                              issue.project, issue)
-      xml.link    href: namespace_project_issue_url(issue.project.namespace,
-                                                    issue.project, issue)
-      xml.title   truncate(issue.title, length: 80)
-      xml.updated issue.created_at.xmlschema
-      xml.media   :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(issue.author_email))
-      xml.author do |author|
-        xml.name issue.author_name
-        xml.email issue.author_email
-      end
-      xml.summary issue.title
-    end
-  end
-
   def merge_requests_sentence(merge_requests)
     # Sorting based on the `!123` or `group/project!123` reference will sort
     # local merge requests first.
@@ -162,16 +145,14 @@ module IssuesHelper
     end
   end
 
-  def emoji_author_list(notes, current_user)
-    list = notes.map do |note|
-             note.author == current_user ? "me" : note.author.name
-           end
-
-    list.join(", ")
+  def award_user_list(awards, current_user)
+    awards.map do |award|
+      award.user == current_user ? 'me' : award.user.name
+    end.join(', ')
   end
 
-  def note_active_class(notes, current_user)
-    if current_user && notes.pluck(:author_id).include?(current_user.id)
+  def award_active_class(awards, current_user)
+    if current_user && awards.find { |a| a.user_id == current_user.id }
       "active"
     else
       ""
diff --git a/app/helpers/javascript_helper.rb b/app/helpers/javascript_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..91dd91718dcc920f33f75069227681b29f520558
--- /dev/null
+++ b/app/helpers/javascript_helper.rb
@@ -0,0 +1,7 @@
+module JavascriptHelper
+  def page_specific_javascripts(js = nil)
+    @page_specific_javascripts = js unless js.nil?
+
+    @page_specific_javascripts
+  end
+end
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index c99b137cdaa74c819669e54e64fb70654918dc11..5074e645769bda3ded7e3c6dc9309279c9423d0d 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -32,7 +32,7 @@ module LabelsHelper
   #   link_to_label(label) { "My Custom Label Text" }
   #
   # Returns a String
-  def link_to_label(label, project: nil, type: :issue, tooltip: true, &block)
+  def link_to_label(label, project: nil, type: :issue, tooltip: true, css_class: nil, &block)
     project ||= @project || label.project
     link = send("namespace_project_#{type.to_s.pluralize}_path",
                 project.namespace,
@@ -40,9 +40,9 @@ module LabelsHelper
                 label_name: [label.name])
 
     if block_given?
-      link_to link, &block
+      link_to link, class: css_class, &block
     else
-      link_to render_colored_label(label, tooltip: tooltip), link
+      link_to render_colored_label(label, tooltip: tooltip), link, class: css_class
     end
   end
 
diff --git a/app/helpers/license_helper.rb b/app/helpers/license_helper.rb
index 90c0def8f926edbdbcf7282609731d45a0f7293f..41574afed9e470f541b776710b557c95c6d49210 100644
--- a/app/helpers/license_helper.rb
+++ b/app/helpers/license_helper.rb
@@ -37,15 +37,13 @@ module LicenseHelper
 
     return unless signed_in
 
-    return unless (is_admin && (license.notify_admins? || license.warn_upgrade_license_message?)) || license.notify_users?
+    return unless (is_admin && license.notify_admins?) || license.notify_users?
 
     message = []
 
-    unless license.warn_upgrade_license_message?
-      message << "The GitLab Enterprise Edition license"
-      message << (license.expired? ? "expired" : "will expire")
-      message << "on #{license.expires_at}."
-    end
+    message << "The GitLab Enterprise Edition license"
+    message << (license.expired? ? "expired" : "will expire")
+    message << "on #{license.expires_at}."
 
     if license.expired? && license.will_block_changes?
       message << "Pushing code and creation of issues and merge requests"
@@ -67,12 +65,6 @@ module LicenseHelper
       message << "to"
       message << (license.block_changes? ? "restore" : "ensure uninterrupted")
       message << "service."
-    elsif license.warn_upgrade_license_message?
-      message << "Your GitLab license currently covers #{license.user_count}"
-      message << "users, but it looks like your site has grown to"
-      message << "#{current_active_user_count} users. Please contact"
-      message << "sales@gitlab.com to increase the number of licensed users."
-      message << "Note: This message is only visible to you as an admin."
     end
 
     message.join(" ")
diff --git a/app/helpers/members_helper.rb b/app/helpers/members_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a53828ef4e7d056f6e63178c8a206d9a0de65c73
--- /dev/null
+++ b/app/helpers/members_helper.rb
@@ -0,0 +1,45 @@
+module MembersHelper
+  # Returns a `<action>_<source>_member` association, e.g.:
+  # - admin_project_member, update_project_member, destroy_project_member
+  # - admin_group_member, update_group_member, destroy_group_member
+  def action_member_permission(action, member)
+    "#{action}_#{member.type.underscore}".to_sym
+  end
+
+  def can_see_member_roles?(source:, user: nil)
+    return false unless user
+
+    user.is_admin? || source.members.exists?(user_id: user.id)
+  end
+
+  def remove_member_message(member, user: nil)
+    user = current_user if defined?(current_user)
+
+    text = 'Are you sure you want to '
+    action =
+      if member.request?
+        if member.user == user
+          'withdraw your access request for'
+        else
+          "deny #{member.user.name}'s request to join"
+        end
+      elsif member.invite?
+        "revoke the invitation for #{member.invite_email} to join"
+      else
+        "remove #{member.user.name} from"
+      end
+
+    text << action << " the #{member.source.human_name} #{member.real_source_type.humanize(capitalize: false)}?"
+  end
+
+  def remove_member_title(member)
+    text = " from #{member.real_source_type.humanize(capitalize: false)}"
+
+    text.prepend(member.request? ? 'Deny access request' : 'Remove user')
+  end
+
+  def leave_confirmation_message(member_source)
+    "Are you sure you want to leave the " \
+    "\"#{member_source.human_name}\" #{member_source.class.to_s.humanize(capitalize: false)}?"
+  end
+end
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index c5b96e13a15ea35d3cf68f01197e048c44dfc420..8a63e7062685033a89cfa4904709250dbbd03705 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -93,10 +93,9 @@ module MergeRequestsHelper
       more_approvals = merge_request.approvals_left - merge_request.approvers_left.count
       approvers_names = merge_request.approvers_left.map(&:name)
 
-      case true
-      when more_approvals > 0
+      if more_approvals > 0
         str << " (from #{render_items_list(approvers_names + ["#{more_approvals} more"])})"
-      when more_approvals < 0
+      elsif more_approvals < 0
         str << " (from #{render_items_list(approvers_names, "or")})"
       else
         str << " (from #{render_items_list(approvers_names)})"
diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb
index 87fc2db69016cc307f978b2eda0259639a163deb..b3e6e468ecd475b84f7ca502f64b64fd6442ad4e 100644
--- a/app/helpers/milestones_helper.rb
+++ b/app/helpers/milestones_helper.rb
@@ -56,7 +56,7 @@ module MilestonesHelper
 
   def milestone_remaining_days(milestone)
     if milestone.expired?
-      content_tag(:strong, 'expired')
+      content_tag(:strong, 'Past due')
     elsif milestone.due_date
       days    = milestone.remaining_days
       content = content_tag(:strong, days)
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index fbb799eecd37d6fb1bbf2ceaef61dc244a176d80..3ff8be5e284cbe4126a16e309d46be7ce071973b 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -12,10 +12,10 @@ module NavHelper
   end
 
   def page_sidebar_class
-    if nav_menu_collapsed?
-      "page-sidebar-collapsed"
+    if pinned_nav?
+      "page-sidebar-expanded page-sidebar-pinned"
     else
-      "page-sidebar-expanded"
+      "page-sidebar-collapsed"
     end
   end
 
@@ -30,17 +30,21 @@ module NavHelper
       else
         "page-gutter right-sidebar-expanded"
       end
+    elsif current_path?('builds#show')
+      "page-gutter build-sidebar right-sidebar-expanded"
     end
   end
 
   def nav_header_class
-    class_name =
-      if nav_menu_collapsed?
-        "header-collapsed"
-      else
-        "header-expanded"
-      end
-    class_name += " with-horizontal-nav" if defined?(nav) && nav
+    class_name = ''
+    class_name << " with-horizontal-nav" if defined?(nav) && nav
+
+    if pinned_nav?
+      class_name << " header-expanded header-pinned-nav"
+    else
+      class_name << " header-collapsed"
+    end
+
     class_name
   end
 
@@ -48,7 +52,11 @@ module NavHelper
     "page-with-layout-nav" if defined?(nav) && nav
   end
 
-  def layout_dropdown_class
-    "controls-dropdown-visible" if current_user
+  def nav_control_class
+    "nav-control" if current_user
+  end
+
+  def pinned_nav?
+    cookies[:pin_nav] == 'true'
   end
 end
diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb
index 54ab9179efc00f121ad3d920b800392e39bc0ccc..50c21fc0d49da9af38ba75550c40902cb84dd698 100644
--- a/app/helpers/notifications_helper.rb
+++ b/app/helpers/notifications_helper.rb
@@ -31,6 +31,21 @@ module NotificationsHelper
     end
   end
 
+  def notification_description(level)
+    case level.to_sym
+    when :participating
+      'You will only receive notifications from related resources'
+    when :mention
+      'You will receive notifications only for comments in which you were @mentioned'
+    when :watch
+      'You will receive notifications for any activity'
+    when :disabled
+      'You will not get any notifications via email'
+    when :global
+      'Use your global notification setting'
+    end
+  end
+
   def notification_list_item(level, setting)
     title = notification_title(level)
 
@@ -39,10 +54,30 @@ module NotificationsHelper
       notification_title: title
     }
 
-    content_tag(:li, class: ('active' if setting.level == level)) do
-      link_to '#', class: 'update-notification', data: data do
-        notification_icon(level, title)
+    content_tag(:li, role: "menuitem") do
+      link_to '#', class: "update-notification #{('is-active' if setting.level == level)}", data: data do
+        link_output = content_tag(:strong, title, class: 'dropdown-menu-inner-title')
+        link_output << content_tag(:span, notification_description(level), class: 'dropdown-menu-inner-content')
+      end
+    end
+  end
+
+  def notification_level_radio_buttons
+    html = ""
+
+    NotificationSetting.levels.each_key do |level|
+      level = level.to_sym
+      next if level == :global
+
+      html << content_tag(:div, class: "radio") do
+        content_tag(:label, { value: level }) do
+          radio_button_tag(:"global_notification_setting[level]", level, @global_notification_setting.level.to_sym == level) +
+          content_tag(:div, level.to_s.capitalize, class: "level-title") +
+          content_tag(:p, notification_description(level))
+        end
       end
     end
+
+    html.html_safe
   end
 end
diff --git a/app/helpers/path_locks_helper.rb b/app/helpers/path_locks_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..20c2adbe96232820890ad592105d1d2adc4b8e70
--- /dev/null
+++ b/app/helpers/path_locks_helper.rb
@@ -0,0 +1,9 @@
+module PathLocksHelper
+  def can_unlock?(path_lock, current_user = @current_user)
+    can?(current_user, :admin_locks, path_lock) || path_lock.user == current_user
+  end
+
+  def license_allows_file_locks?
+    ::License.current && ::License.current.add_on?('GitLab_FileLocks')
+  end
+end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 7d894eeb02c83a7c953403c715217bc5f9e741c6..a1d3c8c5bdc8e647e340b3e502a20aa9f49dd4da 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -1,12 +1,4 @@
 module ProjectsHelper
-  def remove_from_project_team_message(project, member)
-    if member.user
-      "You are going to remove #{member.user.name} from #{project.name} project team. Are you sure?"
-    else
-      "You are going to revoke the invitation for #{member.invite_email} to join #{project.name} project team. Are you sure?"
-    end
-  end
-
   def link_to_project(project)
     link_to [project.namespace.becomes(Namespace), project], title: h(project.name) do
       title = content_tag(:span, project.name, class: 'project-name')
@@ -49,7 +41,7 @@ module ProjectsHelper
     author_html = author_html.html_safe
 
     if opts[:name]
-      link_to(author_html, user_path(author), class: "author_link #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe
+      link_to(author_html, user_path(author), class: "author_link #{"#{opts[:extra_class]}" if opts[:extra_class]} #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe
     else
       title = opts[:title].sub(":name", sanitize(author.name))
       link_to(author_html, user_path(author), class: "author_link has-tooltip", title: title, data: { container: 'body' } ).html_safe
@@ -115,14 +107,6 @@ module ProjectsHelper
     end
   end
 
-  def user_max_access_in_project(user_id, project)
-    level = project.team.max_member_access(user_id)
-
-    if level
-      Gitlab::Access.options_with_owner.key(level)
-    end
-  end
-
   def license_short_name(project)
     return 'LICENSE' if project.repository.license_key.nil?
 
@@ -156,6 +140,10 @@ module ProjectsHelper
       nav_tabs << :container_registry
     end
 
+    if can?(current_user, :read_environment, project)
+      nav_tabs << :environments
+    end
+
     if can?(current_user, :admin_project, project)
       nav_tabs << :settings
     end
@@ -303,10 +291,6 @@ module ProjectsHelper
     end
   end
 
-  def leave_project_message(project)
-    "Are you sure you want to leave \"#{project.name}\" project?"
-  end
-
   def new_readme_path
     ref = @repository.root_ref if @repository
     ref ||= 'master'
diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb
index 5612bca264e02aff3a6d68bd0b7f938fb8f0dbf1..32d1b1533429ee64d6473c8d1c7cd05abe6d9f04 100644
--- a/app/helpers/sorting_helper.rb
+++ b/app/helpers/sorting_helper.rb
@@ -16,7 +16,8 @@ module SortingHelper
       sort_value_downvotes => sort_title_downvotes,
       sort_value_upvotes => sort_title_upvotes,
       sort_value_more_weight => sort_title_more_weight,
-      sort_value_less_weight => sort_title_less_weight
+      sort_value_less_weight => sort_title_less_weight,
+      sort_value_priority => sort_title_priority
     }
   end
 
@@ -30,6 +31,10 @@ module SortingHelper
     }
   end
 
+  def sort_title_priority
+    'Priority'
+  end
+
   def sort_title_oldest_updated
     'Oldest updated'
   end
@@ -94,6 +99,10 @@ module SortingHelper
     'Less weight'
   end
 
+  def sort_value_priority
+    'priority'
+  end
+
   def sort_value_oldest_updated
     'updated_asc'
   end
diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb
index a80dab163badf49520e8a7f7247b04fadb32d827..e4351b991e13f0fde47ed0a959b68f4388c3a08a 100644
--- a/app/helpers/tab_helper.rb
+++ b/app/helpers/tab_helper.rb
@@ -95,7 +95,9 @@ module TabHelper
   end
 
   def project_tab_class
-    return "active" if current_page?(controller: "/projects", action: :edit, id: @project)
+    if controller.controller_path.start_with?('projects')
+      return 'active'
+    end
 
     if ['services', 'hooks', 'deploy_keys', 'protected_branches', 'git_hooks'].include? controller.controller_name
       "active"
@@ -112,7 +114,7 @@ module TabHelper
   end
 
   def profile_tab_class
-    if controller.controller_path =~ /\Aprofiles/
+    if controller.controller_path.start_with?('profiles')
       return 'active'
     end
 
diff --git a/app/helpers/time_helper.rb b/app/helpers/time_helper.rb
index 8142f733e76cd1f6ba4cd0b88afd2171d2eea0f0..b04b0a5114c2b46f2e83f8027edae042aa2d0814 100644
--- a/app/helpers/time_helper.rb
+++ b/app/helpers/time_helper.rb
@@ -20,7 +20,6 @@ module TimeHelper
     end
   end
 
-
   def date_from_to(from, to)
     "#{from.to_s(:short)} - #{to.to_s(:short)}"
   end
diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb
index 81b9b5d7052585c6e0a9b0efcd0838a42cb6d642..9adf5ef29f7db20d75b6d096442de3d1e29d88a1 100644
--- a/app/helpers/todos_helper.rb
+++ b/app/helpers/todos_helper.rb
@@ -12,12 +12,15 @@ module TodosHelper
     when Todo::ASSIGNED then 'assigned you'
     when Todo::MENTIONED then 'mentioned you on'
     when Todo::BUILD_FAILED then 'The build failed for your'
+    when Todo::MARKED then 'marked this as a Todo for'
     end
   end
 
   def todo_target_link(todo)
     target = todo.target_type.titleize.downcase
-    link_to "#{target} #{todo.target_reference}", todo_target_path(todo), { title: todo.target.title }
+    link_to "#{target} #{todo.target_reference}", todo_target_path(todo),
+      class: 'has-tooltip',
+      title: todo.target.title
   end
 
   def todo_target_path(todo)
@@ -37,6 +40,16 @@ module TodosHelper
     end
   end
 
+  def todo_target_state_pill(todo)
+    return unless show_todo_state?(todo)
+
+    content_tag(:span, nil, class: 'target-status') do
+      content_tag(:span, nil, class: "status-box status-box-#{todo.target.state.dasherize}") do
+        todo.target.state.capitalize
+      end
+    end
+  end
+
   def todos_filter_params
     {
       state:      params[:state],
@@ -95,4 +108,10 @@ module TodosHelper
 
     options_from_collection_for_select(types, 'name', 'title', params[:type])
   end
+
+  private
+
+  def show_todo_state?(todo)
+    (todo.target.is_a?(MergeRequest) || todo.target.is_a?(Issue)) && ['closed', 'merged'].include?(todo.target.state)
+  end
 end
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index dbedf417fa5a1eda4998901954b0257c00dcd42f..5c29dce0fe7020ab38a755cdf6c384fcf8e4c80a 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -123,4 +123,20 @@ module TreeHelper
       return tree.name
     end
   end
+
+  def lock_file_link(project = @project, path = @path, html_options: {})
+    return unless license_allows_file_locks?
+    return if path.blank?
+
+    path_lock = project.path_lock_info(path, exact_match: true)
+
+    # Check permissions to unlock
+    return if path_lock && !can_unlock?(path_lock)
+    # Check permissions to lock
+    return if !path_lock && !can?(current_user, :push_code, project)
+
+    label = path_lock ? 'Unlock' : 'Lock'
+    html_options = html_options.merge(data: { state: label.downcase })
+    link_to label, '#', html_options
+  end
 end
diff --git a/app/helpers/workhorse_helper.rb b/app/helpers/workhorse_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2bd0dbfd0957e28abbee5df4154c9caae007dff4
--- /dev/null
+++ b/app/helpers/workhorse_helper.rb
@@ -0,0 +1,24 @@
+# Helpers to send Git blobs, diffs or archives through Workhorse.
+# Workhorse will also serve files when using `send_file`.
+module WorkhorseHelper
+  # Send a Git blob through Workhorse
+  def send_git_blob(repository, blob)
+    headers.store(*Gitlab::Workhorse.send_git_blob(repository, blob))
+    headers['Content-Disposition'] = 'inline'
+    headers['Content-Type'] = safe_content_type(blob)
+    head :ok # 'render nothing: true' messes up the Content-Type
+  end
+
+  # Send a Git diff through Workhorse
+  def send_git_diff(repository, diff_refs)
+    headers.store(*Gitlab::Workhorse.send_git_diff(repository, diff_refs))
+    headers['Content-Disposition'] = 'inline'
+    head :ok
+  end
+
+  # Archive a Git repository and send it through Workhorse
+  def send_git_archive(repository, ref:, format:)
+    headers.store(*Gitlab::Workhorse.send_git_archive(repository, ref: ref, format: format))
+    head :ok
+  end
+end
diff --git a/app/mailers/devise_mailer.rb b/app/mailers/devise_mailer.rb
index b616add283a8b41a17d27a7327b08562dab11cf2..415f6e12885805bb38fd91f9883760e5b64dea39 100644
--- a/app/mailers/devise_mailer.rb
+++ b/app/mailers/devise_mailer.rb
@@ -1,4 +1,6 @@
 class DeviseMailer < Devise::Mailer
   default from: "#{Gitlab.config.gitlab.email_display_name} <#{Gitlab.config.gitlab.email_from}>"
   default reply_to: Gitlab.config.gitlab.email_reply_to
+
+  layout 'devise_mailer'
 end
diff --git a/app/mailers/emails/groups.rb b/app/mailers/emails/groups.rb
deleted file mode 100644
index 1c43f95dc8c61529b0ea4c18fadf91692f0d3575..0000000000000000000000000000000000000000
--- a/app/mailers/emails/groups.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-module Emails
-  module Groups
-    def group_access_granted_email(group_member_id)
-      @group_member = GroupMember.find(group_member_id)
-      @group = @group_member.group
-
-      @target_url = group_url(@group)
-      @current_user = @group_member.user
-
-      mail(to: @group_member.user.notification_email,
-           subject: subject("Access to group was granted"))
-    end
-
-    def group_member_invited_email(group_member_id, token)
-      @group_member = GroupMember.find group_member_id
-      @group = @group_member.group
-      @token = token
-
-      @target_url = group_url(@group)
-      @current_user = @group_member.user
-
-      mail(to: @group_member.invite_email,
-           subject: "Invitation to join group #{@group.name}")
-    end
-
-    def group_invite_accepted_email(group_member_id)
-      @group_member = GroupMember.find group_member_id
-      return if @group_member.created_by.nil?
-
-      @group = @group_member.group
-
-      @target_url = group_url(@group)
-      @current_user = @group_member.created_by
-
-      mail(to: @group_member.created_by.notification_email,
-           subject: subject("Invitation accepted"))
-    end
-
-    def group_invite_declined_email(group_id, invite_email, access_level, created_by_id)
-      return if created_by_id.nil?
-
-      @group = Group.find(group_id)
-      @current_user = @created_by = User.find(created_by_id)
-      @access_level = access_level
-      @invite_email = invite_email
-      
-      @target_url = group_url(@group)
-      mail(to: @created_by.notification_email,
-           subject: subject("Invitation declined"))
-    end
-  end
-end
diff --git a/app/mailers/emails/members.rb b/app/mailers/emails/members.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6dde2e9847db95e5b408e28b8859712334c40ca1
--- /dev/null
+++ b/app/mailers/emails/members.rb
@@ -0,0 +1,81 @@
+module Emails
+  module Members
+    extend ActiveSupport::Concern
+    include MembersHelper
+
+    included do
+      helper_method :member_source, :member
+    end
+
+    def member_access_requested_email(member_source_type, member_id)
+      @member_source_type = member_source_type
+      @member_id = member_id
+
+      admins = member_source.members.owners_and_masters.includes(:user).pluck(:notification_email)
+
+      mail(to: admins,
+           subject: subject("Request to join the #{member_source.human_name} #{member_source.model_name.singular}"))
+    end
+
+    def member_access_granted_email(member_source_type, member_id)
+      @member_source_type = member_source_type
+      @member_id = member_id
+
+      mail(to: member.user.notification_email,
+           subject: subject("Access to the #{member_source.human_name} #{member_source.model_name.singular} was granted"))
+    end
+
+    def member_access_denied_email(member_source_type, source_id, user_id)
+      @member_source_type = member_source_type
+      @member_source = member_source_class.find(source_id)
+      requester = User.find(user_id)
+
+      mail(to: requester.notification_email,
+           subject: subject("Access to the #{member_source.human_name} #{member_source.model_name.singular} was denied"))
+    end
+
+    def member_invited_email(member_source_type, member_id, token)
+      @member_source_type = member_source_type
+      @member_id = member_id
+      @token = token
+
+      mail(to: member.invite_email,
+           subject: "Invitation to join the #{member_source.human_name} #{member_source.model_name.singular}")
+    end
+
+    def member_invite_accepted_email(member_source_type, member_id)
+      @member_source_type = member_source_type
+      @member_id = member_id
+      return unless member.created_by
+
+      mail(to: member.created_by.notification_email,
+           subject: subject('Invitation accepted'))
+    end
+
+    def member_invite_declined_email(member_source_type, source_id, invite_email, created_by_id)
+      return unless created_by_id
+
+      @member_source_type = member_source_type
+      @member_source = member_source_class.find(source_id)
+      @invite_email = invite_email
+      inviter = User.find(created_by_id)
+
+      mail(to: inviter.notification_email,
+           subject: subject('Invitation declined'))
+    end
+
+    def member
+      @member ||= Member.find(@member_id)
+    end
+
+    def member_source
+      @member_source ||= member.source
+    end
+
+    private
+
+    def member_source_class
+      @member_source_type.classify.constantize
+    end
+  end
+end
diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb
index f890dff0d8b1dad72353bd42e30dd44d32d1f415..596f52e5680934ead17170d25ffb28c7d19e2e0f 100644
--- a/app/mailers/emails/projects.rb
+++ b/app/mailers/emails/projects.rb
@@ -1,55 +1,5 @@
 module Emails
   module Projects
-    def project_access_granted_email(project_member_id)
-      @project_member = ProjectMember.find project_member_id
-      @project = @project_member.project
-
-      @target_url = namespace_project_url(@project.namespace, @project)
-      @current_user = @project_member.user
-
-      mail(to: @project_member.user.notification_email,
-           subject: subject("Access to project was granted"))
-    end
-
-    def project_member_invited_email(project_member_id, token)
-      @project_member = ProjectMember.find project_member_id
-      @project = @project_member.project
-      @token = token
-
-      @target_url = namespace_project_url(@project.namespace, @project)
-      @current_user = @project_member.user
-
-      mail(to: @project_member.invite_email,
-           subject: "Invitation to join project #{@project.name_with_namespace}")
-    end
-
-    def project_invite_accepted_email(project_member_id)
-      @project_member = ProjectMember.find project_member_id
-      return if @project_member.created_by.nil?
-
-      @project = @project_member.project
-
-      @target_url = namespace_project_url(@project.namespace, @project)
-      @current_user = @project_member.created_by
-
-      mail(to: @project_member.created_by.notification_email,
-           subject: subject("Invitation accepted"))
-    end
-
-    def project_invite_declined_email(project_id, invite_email, access_level, created_by_id)
-      return if created_by_id.nil?
-
-      @project = Project.find(project_id)
-      @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,
-           subject: subject("Invitation declined"))
-    end
-
     def project_was_moved_email(project_id, user_id, old_path_with_namespace)
       @current_user = @user = User.find user_id
       @project = Project.find project_id
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index dbe2ae2844dcb4587c8677d4443c70b2953ca452..f64563275fd47d025895b9dc0104d00a857264de 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -7,13 +7,15 @@ class Notify < BaseMailer
   include Emails::Notes
   include Emails::Projects
   include Emails::Profile
-  include Emails::Groups
   include Emails::Builds
+  include Emails::Members
 
   add_template_helper MergeRequestsHelper
   add_template_helper DiffHelper
   add_template_helper BlobHelper
   add_template_helper EmailsHelper
+  add_template_helper MembersHelper
+  add_template_helper GitlabRoutingHelper
 
   def test_email(recipient_email, subject, body)
     mail(to: recipient_email,
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 476456c99a627c71fc383a825e8267e71dcf39db..ecb20c580d95ec21d3d7702a096f75ab5347ca3d 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -10,7 +10,6 @@ class Ability
         when CommitStatus then commit_status_abilities(user, subject)
         when Project then project_abilities(user, subject)
         when Issue then issue_abilities(user, subject)
-        when ExternalIssue then external_issue_abilities(user, subject)
         when Note then note_abilities(user, subject)
         when ProjectSnippet then project_snippet_abilities(user, subject)
         when PersonalSnippet then personal_snippet_abilities(user, subject)
@@ -20,6 +19,7 @@ class Ability
         when GroupMember then group_member_abilities(user, subject)
         when ProjectMember then project_member_abilities(user, subject)
         when User then user_abilities
+        when ExternalIssue, Deployment, Environment then project_abilities(user, subject.project)
         else []
         end.concat(global_abilities(user))
 
@@ -37,20 +37,41 @@ class Ability
       ]
     end
 
+    # Given a list of users and a project this method returns the users that can
+    # read the given project.
+    def users_that_can_read_project(users, project)
+      if project.public?
+        users
+      else
+        users.select do |user|
+          if user.admin?
+            true
+          elsif project.internal? && !user.external?
+            true
+          elsif project.owner == user
+            true
+          elsif project.team.members.include?(user)
+            true
+          else
+            false
+          end
+        end
+      end
+    end
+
     # List of possible abilities for anonymous user
     def anonymous_abilities(user, subject)
-      case true
-      when subject.is_a?(PersonalSnippet)
+      if subject.is_a?(PersonalSnippet)
         anonymous_personal_snippet_abilities(subject)
-      when subject.is_a?(ProjectSnippet)
+      elsif subject.is_a?(ProjectSnippet)
         anonymous_project_snippet_abilities(subject)
-      when subject.is_a?(CommitStatus)
+      elsif subject.is_a?(CommitStatus)
         anonymous_commit_status_abilities(subject)
-      when subject.is_a?(Project) || subject.respond_to?(:project)
+      elsif subject.is_a?(Project) || subject.respond_to?(:project)
         anonymous_project_abilities(subject)
-      when subject.is_a?(Group) || subject.respond_to?(:group)
+      elsif subject.is_a?(Group) || subject.respond_to?(:group)
         anonymous_group_abilities(subject)
-      when subject.is_a?(User)
+      elsif subject.is_a?(User)
         anonymous_user_abilities
       else
         []
@@ -180,6 +201,8 @@ class Ability
         project_report_rules
       elsif team.guest?(user)
         project_guest_rules
+      else
+        []
       end
     end
 
@@ -221,6 +244,8 @@ class Ability
         :read_build,
         :read_container_image,
         :read_pipeline,
+        :read_environment,
+        :read_deployment
       ]
     end
 
@@ -239,6 +264,8 @@ class Ability
         :push_code,
         :create_container_image,
         :update_container_image,
+        :create_environment,
+        :create_deployment
       ]
     end
 
@@ -258,6 +285,8 @@ class Ability
         :push_code_to_protected_branches,
         :update_project_snippet,
         :update_pages,
+        :update_environment,
+        :update_deployment,
         :admin_milestone,
         :admin_project_snippet,
         :admin_project_member,
@@ -270,6 +299,9 @@ class Ability
         :admin_container_image,
         :admin_pages,
         :admin_pipeline,
+        :admin_environment,
+        :admin_deployment,
+        :admin_locks
       ]
     end
 
@@ -314,6 +346,8 @@ class Ability
       unless project.builds_enabled
         rules += named_abilities('build')
         rules += named_abilities('pipeline')
+        rules += named_abilities('environment')
+        rules += named_abilities('deployment')
       end
 
       unless project.container_registry_enabled
@@ -512,10 +546,6 @@ class Ability
                      end
     end
 
-    def external_issue_abilities(user, subject)
-      project_abilities(user, subject.project)
-    end
-
     private
 
     def restricted_public_level?
@@ -534,7 +564,7 @@ class Ability
     def filter_confidential_issues_abilities(user, issue, rules)
       return rules if user.admin? || !issue.confidential?
 
-      unless issue.author == user || issue.assignee == user || issue.project.team.member?(user.id)
+      unless issue.author == user || issue.assignee == user || issue.project.team.member?(user, Gitlab::Access::REPORTER)
         rules.delete(:admin_issue)
         rules.delete(:read_issue)
         rules.delete(:update_issue)
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index f5079f92444ced909ec9de1c14ec9a747d2b4575..54bbc3ef40897d2b99f556740e418eefd8e4316f 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -7,7 +7,7 @@ class ApplicationSetting < ActiveRecord::Base
 
   serialize :restricted_visibility_levels
   serialize :import_sources
-  serialize :disabled_oauth_sign_in_sources
+  serialize :disabled_oauth_sign_in_sources, Array
   serialize :restricted_signup_domains, Array
   attr_accessor :restricted_signup_domains_raw
 
@@ -51,6 +51,18 @@ class ApplicationSetting < ActiveRecord::Base
             presence: true,
             numericality: { only_integer: true, greater_than: 0 }
 
+  validates :container_registry_token_expire_delay,
+            presence: true,
+            numericality: { only_integer: true, greater_than: 0 }
+
+  validates :elasticsearch_host,
+            presence: { message: "can't be blank when indexing is enabled" },
+            if: :elasticsearch_indexing?
+
+  validates :elasticsearch_port,
+            presence: { message: "can't be blank when indexing is enabled" },
+            if: :elasticsearch_indexing?
+
   validates_each :restricted_visibility_levels do |record, attr, value|
     unless value.nil?
       value.each do |level|
@@ -98,6 +110,10 @@ class ApplicationSetting < ActiveRecord::Base
     Rails.cache.delete(CACHE_KEY)
   end
 
+  def self.cached
+    Rails.cache.fetch(CACHE_KEY)
+  end
+
   def self.create_from_defaults
     create(
       default_projects_limit: Settings.gitlab['default_projects_limit'],
@@ -105,7 +121,10 @@ class ApplicationSetting < ActiveRecord::Base
       signup_enabled: Settings.gitlab['signup_enabled'],
       signin_enabled: Settings.gitlab['signin_enabled'],
       gravatar_enabled: Settings.gravatar['enabled'],
-      sign_in_text: Settings.extra['sign_in_text'],
+      sign_in_text: nil,
+      after_sign_up_text: nil,
+      help_page_text: nil,
+      shared_runners_text: nil,
       restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
       max_attachment_size: Settings.gitlab['max_attachment_size'],
       session_expire_delay: Settings.gitlab['session_expire_delay'],
@@ -121,10 +140,17 @@ class ApplicationSetting < ActiveRecord::Base
       akismet_enabled: false,
       repository_checks_enabled: true,
       disabled_oauth_sign_in_sources: [],
-      send_user_confirmation_email: false
+      send_user_confirmation_email: false,
+      container_registry_token_expire_delay: 5,
+      elasticsearch_host: ENV['ELASTIC_HOST'] || 'localhost',
+      elasticsearch_port: ENV['ELASTIC_PORT'] || '9200'
     )
   end
 
+  def elasticsearch_host
+    read_attribute(:elasticsearch_host).split(',').map(&:strip)
+  end
+
   def home_page_url_column_exist
     ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url)
   end
diff --git a/app/models/award_emoji.rb b/app/models/award_emoji.rb
new file mode 100644
index 0000000000000000000000000000000000000000..59c7d87f5dfc8d2b821c507da2e01a0d0c1a8288
--- /dev/null
+++ b/app/models/award_emoji.rb
@@ -0,0 +1,26 @@
+class AwardEmoji < ActiveRecord::Base
+  DOWNVOTE_NAME = "thumbsdown".freeze
+  UPVOTE_NAME   = "thumbsup".freeze
+
+  include Participable
+
+  belongs_to :awardable, polymorphic: true
+  belongs_to :user
+
+  validates :awardable, :user, presence: true
+  validates :name, presence: true, inclusion: { in: Emoji.emojis_names }
+  validates :name, uniqueness: { scope: [:user, :awardable_type, :awardable_id] }
+
+  participant :user
+
+  scope :downvotes, -> { where(name: DOWNVOTE_NAME) }
+  scope :upvotes,   -> { where(name: UPVOTE_NAME) }
+
+  def downvote?
+    self.name == DOWNVOTE_NAME
+  end
+
+  def upvote?
+    self.name == UPVOTE_NAME
+  end
+end
diff --git a/app/models/blob.rb b/app/models/blob.rb
index 0fea6b7f57699cc2db2f47d736a6531660ff664d..4279ea2ce578849d80d63da3982e4d5883babfd4 100644
--- a/app/models/blob.rb
+++ b/app/models/blob.rb
@@ -24,7 +24,7 @@ class Blob < SimpleDelegator
   end
 
   def only_display_raw?
-    size && size > 5.megabytes
+    size && truncated?
   end
 
   def svg?
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 460016125912c77fa9a8b85a45dbf5556ba9884e..4f9f42d0ff045affae1debcc0e81b2bdcadfdacb 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -11,6 +11,8 @@ module Ci
 
     scope :unstarted, ->() { where(runner_id: nil) }
     scope :ignore_failures, ->() { where(allow_failure: false) }
+    scope :with_artifacts, ->() { where.not(artifacts_file: nil) }
+    scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) }
 
     mount_uploader :artifacts_file, ArtifactUploader
     mount_uploader :artifacts_metadata, ArtifactUploader
@@ -38,20 +40,21 @@ module Ci
         new_build.save
       end
 
-      def retry(build)
+      def retry(build, user = nil)
         new_build = Ci::Build.new(status: 'pending')
         new_build.ref = build.ref
         new_build.tag = build.tag
         new_build.options = build.options
         new_build.commands = build.commands
         new_build.tag_list = build.tag_list
-        new_build.gl_project_id = build.gl_project_id
-        new_build.commit_id = build.commit_id
+        new_build.project = build.project
+        new_build.pipeline = build.pipeline
         new_build.name = build.name
         new_build.allow_failure = build.allow_failure
         new_build.stage = build.stage
         new_build.stage_idx = build.stage_idx
         new_build.trigger_request = build.trigger_request
+        new_build.user = user
         new_build.save
         MergeRequests::AddTodoWhenBuildFailsService.new(build.project, nil).close(new_build)
         new_build
@@ -66,13 +69,24 @@ module Ci
       # We use around_transition to create builds for next stage as soon as possible, before the `after_*` is executed
       around_transition any => [:success, :failed, :canceled] do |build, block|
         block.call
-        build.commit.create_next_builds(build) if build.commit
+        build.pipeline.create_next_builds(build) if build.pipeline
       end
 
       after_transition any => [:success, :failed, :canceled] do |build|
         build.update_coverage
         build.execute_hooks
       end
+
+      after_transition any => [:success] do |build|
+        if build.environment.present?
+          service = CreateDeploymentService.new(build.project, build.user,
+                                                environment: build.environment,
+                                                sha: build.sha,
+                                                ref: build.ref,
+                                                tag: build.tag)
+          service.execute(build)
+        end
+      end
     end
 
     def retryable?
@@ -80,16 +94,12 @@ module Ci
     end
 
     def retried?
-      !self.commit.statuses.latest.include?(self)
-    end
-
-    def retry
-      Ci::Build.retry(self)
+      !self.pipeline.statuses.latest.include?(self)
     end
 
     def depends_on_builds
       # Get builds of the same type
-      latest_builds = self.commit.builds.latest
+      latest_builds = self.pipeline.builds.latest
 
       # Return builds from previous stages
       latest_builds.where('stage_idx < ?', stage_idx)
@@ -114,11 +124,11 @@ module Ci
 
     def merge_request
       merge_requests = MergeRequest.includes(:merge_request_diff)
-                                   .where(source_branch: ref, source_project_id: commit.gl_project_id)
+                                   .where(source_branch: ref, source_project_id: pipeline.gl_project_id)
                                    .reorder(iid: :asc)
 
       merge_requests.find do |merge_request|
-        merge_request.commits.any? { |ci| ci.id == commit.sha }
+        merge_request.commits.any? { |ci| ci.id == pipeline.sha }
       end
     end
 
@@ -194,7 +204,7 @@ module Ci
 
     def trace_length
       if raw_trace
-        raw_trace.length
+        raw_trace.bytesize
       else
         0
       end
@@ -216,7 +226,7 @@ module Ci
       recreate_trace_dir
 
       File.truncate(path_to_trace, offset) if File.exist?(path_to_trace)
-      File.open(path_to_trace, 'a') do |f|
+      File.open(path_to_trace, 'ab') do |f|
         f.write(trace_part)
       end
     end
@@ -286,7 +296,7 @@ module Ci
       project.runners_token
     end
 
-    def valid_token? token
+    def valid_token?(token)
       project.valid_runners_token? token
     end
 
@@ -314,10 +324,11 @@ module Ci
       project.execute_hooks(build_data.dup, :build_hooks)
       project.execute_services(build_data.dup, :build_hooks)
       PagesService.new(build_data).execute
+      project.running_or_pending_build_count(force: true)
     end
 
     def artifacts?
-      artifacts_file.exists?
+      !artifacts_expired? && artifacts_file.exists?
     end
 
     def artifacts_metadata?
@@ -328,11 +339,15 @@ module Ci
       Gitlab::Ci::Build::Artifacts::Metadata.new(artifacts_metadata.path, path, **options).to_entry
     end
 
+    def erase_artifacts!
+      remove_artifacts_file!
+      remove_artifacts_metadata!
+    end
+
     def erase(opts = {})
       return false unless erasable?
 
-      remove_artifacts_file!
-      remove_artifacts_metadata!
+      erase_artifacts!
       erase_trace!
       update_erased!(opts[:erased_by])
     end
@@ -345,6 +360,25 @@ module Ci
       !self.erased_at.nil?
     end
 
+    def artifacts_expired?
+      artifacts_expire_at && artifacts_expire_at < Time.now
+    end
+
+    def artifacts_expire_in
+      artifacts_expire_at - Time.now if artifacts_expire_at
+    end
+
+    def artifacts_expire_in=(value)
+      self.artifacts_expire_at =
+        if value
+          Time.now + ChronicDuration.parse(value)
+        end
+    end
+
+    def keep_artifacts!
+      self.update(artifacts_expire_at: nil)
+    end
+
     private
 
     def erase_trace!
@@ -352,7 +386,7 @@ module Ci
     end
 
     def update_erased!(user = nil)
-      self.update(erased_by: user, erased_at: Time.now)
+      self.update(erased_by: user, erased_at: Time.now, artifacts_expire_at: nil)
     end
 
     def yaml_variables
@@ -360,8 +394,8 @@ module Ci
     end
 
     def global_yaml_variables
-      if commit.config_processor
-        commit.config_processor.global_variables.map do |key, value|
+      if pipeline.config_processor
+        pipeline.config_processor.global_variables.map do |key, value|
           { key: key, value: value, public: true }
         end
       else
@@ -370,8 +404,8 @@ module Ci
     end
 
     def job_yaml_variables
-      if commit.config_processor
-        commit.config_processor.job_variables(name).map do |key, value|
+      if pipeline.config_processor
+        pipeline.config_processor.job_variables(name).map do |key, value|
           { key: key, value: value, public: true }
         end
       else
diff --git a/app/models/ci/commit.rb b/app/models/ci/pipeline.rb
similarity index 86%
rename from app/models/ci/commit.rb
rename to app/models/ci/pipeline.rb
index 6675a3f5d53fefe8de92d372ed093fe16bc2a316..4bbfb4cc8067beec300592fe2528ae0e910b3402 100644
--- a/app/models/ci/commit.rb
+++ b/app/models/ci/pipeline.rb
@@ -1,12 +1,14 @@
 module Ci
-  class Commit < ActiveRecord::Base
+  class Pipeline < ActiveRecord::Base
     extend Ci::Model
     include Statuseable
 
+    self.table_name = 'ci_commits'
+
     belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
-    has_many :statuses, class_name: 'CommitStatus'
-    has_many :builds, class_name: 'Ci::Build'
-    has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest'
+    has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id
+    has_many :builds, class_name: 'Ci::Build', foreign_key: :commit_id
+    has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest', foreign_key: :commit_id
 
     validates_presence_of :sha
     validates_presence_of :status
@@ -21,7 +23,7 @@ module Ci
 
     def self.stages
       # We use pluck here due to problems with MySQL which doesn't allow LIMIT/OFFSET in queries
-      CommitStatus.where(commit: pluck(:id)).stages
+      CommitStatus.where(pipeline: pluck(:id)).stages
     end
 
     def project_id
@@ -47,7 +49,7 @@ module Ci
     end
 
     def short_sha
-      Ci::Commit.truncate_sha(sha)
+      Ci::Pipeline.truncate_sha(sha)
     end
 
     def commit_data
@@ -66,12 +68,18 @@ module Ci
       end
     end
 
+    def cancelable?
+      builds.running_or_pending.any?
+    end
+
     def cancel_running
       builds.running_or_pending.each(&:cancel)
     end
 
-    def retry_failed
-      builds.latest.failed.select(&:retryable?).each(&:retry)
+    def retry_failed(user)
+      builds.latest.failed.select(&:retryable?).each do |build|
+        Ci::Build.retry(build, user)
+      end
     end
 
     def latest?
@@ -155,6 +163,10 @@ module Ci
       git_commit_message =~ /(\[ci skip\])/ if git_commit_message
     end
 
+    def environments
+      builds.where.not(environment: nil).success.pluck(:environment).uniq
+    end
+
     private
 
     def update_state
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 6829dc91cb9130876a1a0f5c3a3cf2fb5740ab02..adb652922085d211e23d9588dc4ff9ae26a250d1 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -60,7 +60,7 @@ module Ci
     end
 
     def display_name
-      return short_sha unless !description.blank?
+      return short_sha if description.blank?
 
       description
     end
diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb
index 872d5fb31de10f7007100668d1b102529e9f4587..b69ae37668c89de576464f679abfb249202fb8d7 100644
--- a/app/models/ci/trigger_request.rb
+++ b/app/models/ci/trigger_request.rb
@@ -3,7 +3,7 @@ module Ci
     extend Ci::Model
     
     belongs_to :trigger, class_name: 'Ci::Trigger'
-    belongs_to :commit, class_name: 'Ci::Commit'
+    belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
     has_many :builds, class_name: 'Ci::Build'
 
     serialize :variables
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index 10802f64813efcb9e89292a5d9cabdfadf863967..f8d5d4486fd43f93c20dea83e76cb8854d2f1099 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -11,6 +11,9 @@ module Ci
       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
+    attr_encrypted :value, 
+       mode: :per_attribute_iv_and_salt,
+       key: Gitlab::Application.secrets.db_key_base,
+       algorithm: 'aes-256-cbc'
   end
 end
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 562c3ed15b211513743cc86dea3addfcf13a21cd..d69d518fadda10c3fb610fc4677c786984803cc4 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -8,7 +8,10 @@ class Commit
   include StaticModel
 
   attr_mentionable :safe_message, pipeline: :single_line
-  participant :author, :committer, :notes
+
+  participant :author
+  participant :committer
+  participant :notes_with_associations
 
   attr_accessor :project
 
@@ -194,6 +197,10 @@ class Commit
     project.notes.for_commit_id(self.id)
   end
 
+  def notes_with_associations
+    notes.includes(:author)
+  end
+
   def method_missing(m, *args, &block)
     @raw.send(m, *args, &block)
   end
@@ -207,19 +214,19 @@ class Commit
     @raw.short_id(7)
   end
 
-  def ci_commits
-    @ci_commits ||= project.ci_commits.where(sha: sha)
+  def pipelines
+    @pipeline ||= project.pipelines.where(sha: sha)
   end
 
   def status
     return @status if defined?(@status)
-    @status ||= ci_commits.status
+    @status ||= pipelines.status
   end
 
   def revert_branch_name
     "revert-#{short_id}"
   end
-  
+
   def cherry_pick_branch_name
     project.repository.next_branch("cherry-pick-#{short_id}", mild: true)
   end
@@ -251,11 +258,13 @@ class Commit
   end
 
   def has_been_reverted?(current_user = nil, noteable = self)
-    Gitlab::ReferenceExtractor.lazily do
-      noteable.notes.system.flat_map do |note|
-        note.all_references(current_user).commits
-      end
-    end.any? { |commit_ref| commit_ref.reverts_commit?(self) }
+    ext = all_references(current_user)
+
+    noteable.notes_with_associations.system.each do |note|
+      note.all_references(current_user, extractor: ext)
+    end
+
+    ext.commits.any? { |commit_ref| commit_ref.reverts_commit?(self) }
   end
 
   def change_type_title
diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb
index 51673897d98849fb9f5b07e54ecc12c74c86b126..4066958f67c0b119e762a33172fd6e99db8340e2 100644
--- a/app/models/commit_range.rb
+++ b/app/models/commit_range.rb
@@ -62,7 +62,7 @@ class CommitRange
   def initialize(range_string, project)
     @project = project
 
-    range_string.strip!
+    range_string = range_string.strip
 
     unless range_string =~ /\A#{PATTERN}\z/
       raise ArgumentError, "invalid CommitRange string format: #{range_string}"
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index f774b6e0efb2b35b5b70a2b0f1de7c037a9a2294..e53c483b904d7abf299d220c07a6189fb87c198d 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -4,10 +4,10 @@ class CommitStatus < ActiveRecord::Base
   self.table_name = 'ci_builds'
 
   belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
-  belongs_to :commit, class_name: 'Ci::Commit', touch: true
+  belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id, touch: true
   belongs_to :user
 
-  validates :commit, presence: true
+  validates :pipeline, presence: true
 
   validates_presence_of :name
 
@@ -44,18 +44,18 @@ class CommitStatus < ActiveRecord::Base
     end
 
     after_transition [:pending, :running] => :success do |commit_status|
-      MergeRequests::MergeWhenBuildSucceedsService.new(commit_status.commit.project, nil).trigger(commit_status)
+      MergeRequests::MergeWhenBuildSucceedsService.new(commit_status.pipeline.project, nil).trigger(commit_status)
     end
 
     after_transition any => :failed do |commit_status|
-      MergeRequests::AddTodoWhenBuildFailsService.new(commit_status.commit.project, nil).execute(commit_status)
+      MergeRequests::AddTodoWhenBuildFailsService.new(commit_status.pipeline.project, nil).execute(commit_status)
     end
   end
 
-  delegate :sha, :short_sha, to: :commit
+  delegate :sha, :short_sha, to: :pipeline
 
   def before_sha
-    commit.before_sha || Gitlab::Git::BLANK_SHA
+    pipeline.before_sha || Gitlab::Git::BLANK_SHA
   end
 
   def self.stages
diff --git a/app/models/concerns/access_requestable.rb b/app/models/concerns/access_requestable.rb
new file mode 100644
index 0000000000000000000000000000000000000000..eedd32a729fb660a8825cd7d5103ca2589e0ca58
--- /dev/null
+++ b/app/models/concerns/access_requestable.rb
@@ -0,0 +1,16 @@
+# == AccessRequestable concern
+#
+# Contains functionality related to objects that can receive request for access.
+#
+# Used by Project, and Group.
+#
+module AccessRequestable
+  extend ActiveSupport::Concern
+
+  def request_access(user)
+    members.create(
+      access_level: Gitlab::Access::DEVELOPER,
+      user: user,
+      requested_at: Time.now.utc)
+  end
+end
diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb
new file mode 100644
index 0000000000000000000000000000000000000000..539c7c31e30f1ee8c42c70bae9716742fb4efbd3
--- /dev/null
+++ b/app/models/concerns/awardable.rb
@@ -0,0 +1,85 @@
+module Awardable
+  extend ActiveSupport::Concern
+
+  included do
+    has_many :award_emoji, as: :awardable, dependent: :destroy
+
+    if self < Participable
+      participant :award_emoji_with_associations
+    end
+  end
+
+  module ClassMethods
+    def order_upvotes_desc
+      order_votes_desc(AwardEmoji::UPVOTE_NAME)
+    end
+
+    def order_downvotes_desc
+      order_votes_desc(AwardEmoji::DOWNVOTE_NAME)
+    end
+
+    def order_votes_desc(emoji_name)
+      awardable_table = self.arel_table
+      awards_table = AwardEmoji.arel_table
+
+      join_clause = awardable_table.join(awards_table, Arel::Nodes::OuterJoin).on(
+        awards_table[:awardable_id].eq(awardable_table[:id]).and(
+          awards_table[:awardable_type].eq(self.name).and(
+            awards_table[:name].eq(emoji_name)
+          )
+        )
+      ).join_sources
+
+      joins(join_clause).group(awardable_table[:id]).reorder("COUNT(award_emoji.id) DESC")
+    end
+  end
+
+  def award_emoji_with_associations
+    award_emoji.includes(:user)
+  end
+
+  def grouped_awards(with_thumbs: true)
+    awards = award_emoji_with_associations.group_by(&:name)
+
+    if with_thumbs
+      awards[AwardEmoji::UPVOTE_NAME]   ||= []
+      awards[AwardEmoji::DOWNVOTE_NAME] ||= []
+    end
+
+    awards
+  end
+
+  def downvotes
+    award_emoji.downvotes.count
+  end
+
+  def upvotes
+    award_emoji.upvotes.count
+  end
+
+  def emoji_awardable?
+    true
+  end
+
+  def awarded_emoji?(emoji_name, current_user)
+    award_emoji.where(name: emoji_name, user: current_user).exists?
+  end
+
+  def create_award_emoji(name, current_user)
+    return unless emoji_awardable?
+
+    award_emoji.create(name: name, user: current_user)
+  end
+
+  def remove_award_emoji(name, current_user)
+    award_emoji.where(name: name, user: current_user).destroy_all
+  end
+
+  def toggle_award_emoji(emoji_name, current_user)
+    if awarded_emoji?(emoji_name, current_user)
+      remove_award_emoji(emoji_name, current_user)
+    else
+      create_award_emoji(emoji_name, current_user)
+    end
+  end
+end
diff --git a/app/models/concerns/elastic/application_search.rb b/app/models/concerns/elastic/application_search.rb
index 9689c86a3b0c89242eca85ceb5e64955baea77a3..27dde0d509981fb2a8237701a1a0c0db4e811bf2 100644
--- a/app/models/concerns/elastic/application_search.rb
+++ b/app/models/concerns/elastic/application_search.rb
@@ -1,15 +1,11 @@
 module Elastic
   module ApplicationSearch
     extend ActiveSupport::Concern
+    extend Gitlab::CurrentSettings
 
     included do
       include Elasticsearch::Model
 
-      self.__elasticsearch__.client = Elasticsearch::Client.new(
-        host: Gitlab.config.elasticsearch.host,
-        port: Gitlab.config.elasticsearch.port
-      )
-
       index_name [Rails.application.class.parent_name.downcase, self.name.downcase, Rails.env].join('-')
 
       settings \
@@ -31,24 +27,29 @@ module Elastic
         }
 
       after_commit on: :create do
-        if Gitlab.config.elasticsearch.enabled && self.searchable?
+        if current_application_settings.elasticsearch_indexing? && self.searchable?
           ElasticIndexerWorker.perform_async(:index, self.class.to_s, self.id)
         end
       end
 
       after_commit on: :update do
-        if Gitlab.config.elasticsearch.enabled && self.searchable?
-          ElasticIndexerWorker.perform_async(:update, self.class.to_s, self.id)
+        if current_application_settings.elasticsearch_indexing? && self.searchable?
+          ElasticIndexerWorker.perform_async(
+            :update,
+            self.class.to_s,
+            self.id,
+            changed_fields: self.previous_changes.keys
+          )
         end
       end
 
       after_commit on: :destroy do
-        if Gitlab.config.elasticsearch.enabled && self.searchable?
+        if current_application_settings.elasticsearch_indexing? && self.searchable?
           ElasticIndexerWorker.perform_async(:delete, self.class.to_s, self.id)
         end
       end
 
-      # Should be overridden in the models where not eveything should be indexed
+      # Should be overridden in the models where some records should be skipped
       def searchable?
         true
       end
diff --git a/app/models/concerns/elastic/issues_search.rb b/app/models/concerns/elastic/issues_search.rb
index 227c682c73c3321dba437274c25b6148402dacda..ca13b11718426b5835990276e7cb9f34d577dfea 100644
--- a/app/models/concerns/elastic/issues_search.rb
+++ b/app/models/concerns/elastic/issues_search.rb
@@ -69,7 +69,7 @@ module Elastic
                                    should: [
                                      { term: { author_id: current_user.id } },
                                      { term: { assignee_id: current_user.id } },
-                                     { terms: { project_id: current_user.authorized_projects.pluck(:id) } }
+                                     { terms: { project_id: current_user.authorized_projects(Gitlab::Access::REPORTER).pluck(:id) } }
                                    ]
                                  }
                                }
diff --git a/app/models/concerns/elastic/notes_search.rb b/app/models/concerns/elastic/notes_search.rb
index 384bb33a6916121a572bd10d935699b36df99b78..aa9da00007e9db65e56ce70eabfb06408953c595 100644
--- a/app/models/concerns/elastic/notes_search.rb
+++ b/app/models/concerns/elastic/notes_search.rb
@@ -12,6 +12,12 @@ module Elastic
         indexes :project_id,  type: :integer
         indexes :created_at,  type: :date
 
+        indexes :issue do
+          indexes :assignee_id, type: :integer
+          indexes :author_id, type: :integer
+          indexes :confidential, type: :boolean
+        end
+
         indexes :updated_at_sort, type: :string, index: 'not_analyzed'
       end
 
@@ -24,27 +30,36 @@ module Elastic
           data[attr.to_s] = self.send(attr)
         end
 
+        if noteable.is_a?(Issue)
+          data['issue'] = {
+            assignee_id: noteable.assignee_id,
+            author_id: noteable.author_id,
+            confidential: noteable.confidential
+          }
+        end
+
         data['updated_at_sort'] = updated_at
         data
       end
 
       def self.elastic_search(query, options: {})
-        options[:in] = ["note"]
+        options[:in] = ['note']
 
         query_hash = {
           query: {
             bool: {
-              must: { match: { note: query } },
+              must: [{ match: { note: query } }],
             },
           }
         }
 
         if query.blank?
-          query_hash[:query][:bool][:must] = { match_all: {} }
+          query_hash[:query][:bool][:must] = [{ match_all: {} }]
           query_hash[:track_scores] = true
         end
 
         query_hash = project_ids_filter(query_hash, options[:project_ids])
+        query_hash = confidentiality_filter(query_hash, options[:current_user])
 
         query_hash[:sort] = [
           { updated_at_sort: { order: :desc, mode: :min } },
@@ -55,6 +70,40 @@ module Elastic
 
         self.__elasticsearch__.search(query_hash)
       end
+
+      def self.confidentiality_filter(query_hash, current_user)
+        return query_hash if current_user && current_user.admin?
+
+        filter = {
+          bool: {
+            should: [
+              { bool: { must_not: [{ exists: { field: :issue } }] } },
+              { term: { "issue.confidential" => false } }
+            ]
+          }
+        }
+
+        if current_user
+          filter[:bool][:should] << {
+            bool: {
+              must: [
+                { term: { "issue.confidential" => true } },
+                { bool: {
+                    should: [
+                      { term: { "issue.author_id" => current_user.id } },
+                      { term: { "issue.assignee_id" => current_user.id } },
+                      { terms: { "project_id" => current_user.authorized_projects(Gitlab::Access::REPORTER).pluck(:id) } }
+                    ]
+                  }
+                }
+              ]
+            }
+          }
+        end
+
+        query_hash[:query][:bool][:must] << filter
+        query_hash
+      end
     end
   end
 end
diff --git a/app/models/concerns/elastic/repositories_search.rb b/app/models/concerns/elastic/repositories_search.rb
index 0a42d754d24970b1949079fb81bf6943fe5b2909..54615d25ab3d5cb5ce726a0eac16ef789e4ffd00 100644
--- a/app/models/concerns/elastic/repositories_search.rb
+++ b/app/models/concerns/elastic/repositories_search.rb
@@ -5,11 +5,6 @@ module Elastic
     included do
       include Elasticsearch::Git::Repository
 
-      self.__elasticsearch__.client = Elasticsearch::Client.new(
-        host: Gitlab.config.elasticsearch.host,
-        port: Gitlab.config.elasticsearch.port
-      )
-
       def repository_id
         project.id
       end
diff --git a/app/models/concerns/elastic/wiki_repositories_search.rb b/app/models/concerns/elastic/wiki_repositories_search.rb
index 979605cce2eb21ab792da6ab7f6e75c78ddf64d1..4e6f8b54c601e8bea5be8daf2ba5416285ee40ac 100644
--- a/app/models/concerns/elastic/wiki_repositories_search.rb
+++ b/app/models/concerns/elastic/wiki_repositories_search.rb
@@ -5,11 +5,6 @@ module Elastic
     included do
       include Elasticsearch::Git::Repository
 
-      self.__elasticsearch__.client = Elasticsearch::Client.new(
-        host: Gitlab.config.elasticsearch.host,
-        port: Gitlab.config.elasticsearch.port
-      )
-
       def repository_id
         "wiki_#{project.id}"
       end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 3a4152cc767a61d057079926ac400b448930d722..b48b555490ab0b437718bcade781d4dcbf5766a8 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -10,13 +10,19 @@ module Issuable
   include Mentionable
   include Subscribable
   include StripAttribute
+  include Awardable
 
   included do
     belongs_to :author, class_name: "User"
     belongs_to :assignee, class_name: "User"
     belongs_to :updated_by, class_name: "User"
     belongs_to :milestone
-    has_many :notes, as: :noteable, dependent: :destroy
+    has_many :notes, as: :noteable, dependent: :destroy do
+      def authors_loaded?
+        # We check first if we're loaded to not load unnecesarily.
+        loaded? && to_a.all? { |note| note.association(:author).loaded? }
+      end
+    end
     has_many :label_links, as: :target, dependent: :destroy
     has_many :labels, through: :label_links
     has_many :todos, as: :target, dependent: :destroy
@@ -31,6 +37,7 @@ module Issuable
     scope :unassigned, -> { where("assignee_id IS NULL") }
     scope :of_projects, ->(ids) { where(project_id: ids) }
     scope :of_milestones, ->(ids) { where(milestone_id: ids) }
+    scope :with_milestone, ->(title) { left_joins_milestones.where(milestones: { title: title }) }
     scope :opened, -> { with_state(:opened, :reopened) }
     scope :only_opened, -> { with_state(:opened) }
     scope :only_reopened, -> { with_state(:reopened) }
@@ -41,10 +48,16 @@ module Issuable
     scope :order_weight_desc, -> { reorder('weight IS NOT NULL, weight DESC') }
     scope :order_weight_asc, -> { reorder('weight ASC') }
 
+    scope :left_joins_milestones,    -> { joins("LEFT OUTER JOIN milestones ON #{table_name}.milestone_id = milestones.id") }
+    scope :order_milestone_due_desc, -> { left_joins_milestones.reorder('milestones.due_date IS NULL, milestones.id IS NULL, milestones.due_date DESC') }
+    scope :order_milestone_due_asc,  -> { left_joins_milestones.reorder('milestones.due_date IS NULL, milestones.id IS NULL, milestones.due_date ASC') }
+
+    scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) }
     scope :join_project, -> { joins(:project) }
+    scope :inc_notes_with_associations, -> { includes(notes: :author) }
     scope :references_project, -> { references(:project) }
     scope :non_archived, -> { join_project.where(projects: { archived: false }) }
-    scope :outer_join_milestone, -> { joins("LEFT OUTER JOIN milestones ON milestones.id = #{table_name}.milestone_id") }
+
 
     delegate :name,
              :email,
@@ -58,11 +71,23 @@ module Issuable
              prefix: true
 
     attr_mentionable :title, pipeline: :single_line
-    attr_mentionable :description, cache: true
-    participant :author, :assignee, :notes_with_associations
+    attr_mentionable :description
+
+    participant :author
+    participant :assignee
+    participant :notes_with_associations
+
     strip_attributes :title
 
     acts_as_paranoid
+
+    after_save :update_assignee_cache_counts, if: :assignee_id_changed?
+
+    def update_assignee_cache_counts
+      # make sure we flush the cache for both the old *and* new assignee
+      User.find(assignee_id_was).update_cache_counts if assignee_id_was
+      assignee.update_cache_counts if assignee
+    end
   end
 
   module ClassMethods
@@ -91,7 +116,7 @@ module Issuable
       where(t[:title].matches(pattern).or(t[:description].matches(pattern)))
     end
 
-    def sort(method)
+    def sort(method, excluded_labels: [])
       case method.to_s
       when 'milestone_due_asc' then order_milestone_due_asc
       when 'milestone_due_desc' then order_milestone_due_desc
@@ -99,40 +124,54 @@ module Issuable
       when 'upvotes_desc' then order_upvotes_desc
       when 'weight_desc' then order_weight_desc
       when 'weight_asc' then order_weight_asc
+      when 'priority' then order_labels_priority(excluded_labels: excluded_labels)
       else
         order_by(method)
       end
     end
 
-    def order_downvotes_desc
-      order_votes_desc('thumbsdown')
+    def order_labels_priority(excluded_labels: [])
+      select("#{table_name}.*, (#{highest_label_priority(excluded_labels).to_sql}) AS highest_priority").
+        group(arel_table[:id]).
+        reorder(Gitlab::Database.nulls_last_order('highest_priority', 'ASC'))
     end
 
-    def order_upvotes_desc
-      order_votes_desc('thumbsup')
+    def with_label(title, sort = nil)
+      if title.is_a?(Array) && title.size > 1
+        joins(:labels).where(labels: { title: title }).group(*grouping_columns(sort)).having("COUNT(DISTINCT labels.title) = #{title.size}")
+      else
+        joins(:labels).where(labels: { title: title })
+      end
     end
 
-    def order_votes_desc(award_emoji_name)
-      issuable_table = self.arel_table
-      note_table = Note.arel_table
-
-      join_clause = issuable_table.join(note_table, Arel::Nodes::OuterJoin).on(
-        note_table[:noteable_id].eq(issuable_table[:id]).and(
-          note_table[:noteable_type].eq(self.name).and(
-            note_table[:is_award].eq(true).and(note_table[:note].eq(award_emoji_name))
-          )
-        )
-      ).join_sources
+    # Includes table keys in group by clause when sorting
+    # preventing errors in postgres
+    #
+    # Returns an array of arel columns
+    def grouping_columns(sort)
+      grouping_columns = [arel_table[:id]]
+
+      if ["milestone_due_desc", "milestone_due_asc"].include?(sort)
+        milestone_table = Milestone.arel_table
+        grouping_columns << milestone_table[:id]
+        grouping_columns << milestone_table[:due_date]
+      end
 
-      joins(join_clause).group(issuable_table[:id]).reorder("COUNT(notes.id) DESC")
+      grouping_columns
     end
 
-    def with_label(title)
-      if title.is_a?(Array) && title.size > 1
-        joins(:labels).where(labels: { title: title }).group(arel_table[:id]).having("COUNT(DISTINCT labels.title) = #{title.size}")
-      else
-        joins(:labels).where(labels: { title: title })
-      end
+    private
+
+    def highest_label_priority(excluded_labels)
+      query = Label.select(Label.arel_table[:priority].minimum).
+        joins(:label_links).
+        where(label_links: { target_type: name }).
+        where("label_links.target_id = #{table_name}.id").
+        reorder(nil)
+
+      query.where.not(title: excluded_labels) if excluded_labels.present?
+
+      query
     end
   end
 
@@ -144,10 +183,6 @@ module Issuable
     today? && created_at == updated_at
   end
 
-  def is_assigned?
-    !!assignee_id
-  end
-
   def is_being_reassigned?
     assignee_id_changed?
   end
@@ -156,16 +191,14 @@ module Issuable
     opened? || reopened?
   end
 
-  def downvotes
-    notes.awards.where(note: "thumbsdown").count
-  end
-
-  def upvotes
-    notes.awards.where(note: "thumbsup").count
-  end
-
   def user_notes_count
-    notes.user.count
+    if notes.loaded?
+      # Use the in-memory association to select and count to avoid hitting the db
+      notes.to_a.count { |note| !note.system? }
+    else
+      # do the count query
+      notes.user.count
+    end
   end
 
   def subscribed_without_subscriptions?(user)
@@ -186,6 +219,10 @@ module Issuable
     hook_data
   end
 
+  def labels_array
+    labels.to_a
+  end
+
   def label_names
     labels.order('title ASC').pluck(:title)
   end
@@ -221,7 +258,13 @@ module Issuable
   end
 
   def notes_with_associations
-    notes.includes(:author, :project)
+    # If A has_many Bs, and B has_many Cs, and you do
+    # `A.includes(b: :c).each { |a| a.b.includes(:c) }`, sadly ActiveRecord
+    # will do the inclusion again. So, we check if all notes in the relation
+    # already have their authors loaded (possibly because the scope
+    # `inc_notes_with_associations` was used) and skip the inclusion if that's
+    # the case.
+    notes.authors_loaded? ? notes : notes.includes(:author)
   end
 
   def updated_tasks
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index b381d22548567233e50406dc5e8da59bb49a621c..f00b5b8497c13ab5bc576cb5d74b7742c9624948 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -23,7 +23,7 @@ module Mentionable
 
   included do
     if self < Participable
-      participant ->(current_user) { mentioned_users(current_user) }
+      participant -> (user, ext) { all_references(user, extractor: ext) }
     end
   end
 
@@ -43,23 +43,22 @@ module Mentionable
     self
   end
 
-  def all_references(current_user = nil, text = nil)
-    ext = Gitlab::ReferenceExtractor.new(self.project, current_user || self.author, self.author)
+  def all_references(current_user = nil, text = nil, extractor: nil)
+    extractor ||= Gitlab::ReferenceExtractor.
+      new(project, current_user || author)
 
     if text
-      ext.analyze(text)
+      extractor.analyze(text, author: author)
     else
       self.class.mentionable_attrs.each do |attr, options|
-        text = send(attr)
+        text = __send__(attr)
+        options = options.merge(cache_key: [self, attr], author: author)
 
-        context = options.dup
-        context[:cache_key] = [self, attr] if context.delete(:cache) && self.persisted?
-
-        ext.analyze(text, context)
+        extractor.analyze(text, options)
       end
     end
 
-    ext
+    extractor
   end
 
   def mentioned_users(current_user = nil)
diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb
index fc6f83b918b4929240bd85acfb9906af1ea51b1a..9056722f45e44f9a18fa62e938e89db26e4ccd65 100644
--- a/app/models/concerns/participable.rb
+++ b/app/models/concerns/participable.rb
@@ -3,8 +3,6 @@
 # Contains functionality related to objects that can have participants, such as
 # an author, an assignee and people mentioned in its description or comments.
 #
-# Used by Issue, Note, MergeRequest, Snippet and Commit.
-#
 # Usage:
 #
 #     class Issue < ActiveRecord::Base
@@ -12,22 +10,36 @@
 #
 #       # ...
 #
-#       participant :author, :assignee, :notes, ->(current_user) { mentioned_users(current_user) }
+#       participant :author
+#       participant :assignee
+#       participant :notes
+#
+#       participant -> (current_user, ext) do
+#         ext.analyze('...')
+#       end
 #     end
 #
 #     issue = Issue.last
 #     users = issue.participants
-#     # `users` will contain the issue's author, its assignee,
-#     # all users returned by its #mentioned_users method,
-#     # as well as all participants to all of the issue's notes,
-#     # since Note implements Participable as well.
-#
 module Participable
   extend ActiveSupport::Concern
 
   module ClassMethods
-    def participant(*attrs)
-      participant_attrs.concat(attrs)
+    # Adds a list of participant attributes. Attributes can either be symbols or
+    # Procs.
+    #
+    # When using a Proc instead of a Symbol the Proc will be given two
+    # arguments:
+    #
+    # 1. The current user (as an instance of User)
+    # 2. An instance of `Gitlab::ReferenceExtractor`
+    #
+    # It is expected that a Proc populates the given reference extractor
+    # instance with data. The return value of the Proc is ignored.
+    #
+    # attr - The name of the attribute or a Proc
+    def participant(attr)
+      participant_attrs << attr
     end
 
     def participant_attrs
@@ -35,42 +47,42 @@ module Participable
     end
   end
 
-  # Be aware that this method makes a lot of sql queries.
-  # Save result into variable if you are going to reuse it inside same request
-  def participants(current_user = self.author)
-    participants =
-      Gitlab::ReferenceExtractor.lazily do
-        self.class.participant_attrs.flat_map do |attr|
-          value =
-            if attr.respond_to?(:call)
-              instance_exec(current_user, &attr)
-            else
-              send(attr)
-            end
+  # Returns the users participating in a discussion.
+  #
+  # This method processes attributes of objects in breadth-first order.
+  #
+  # Returns an Array of User instances.
+  def participants(current_user = nil)
+    current_user ||= author
+    ext = Gitlab::ReferenceExtractor.new(project, current_user)
+    participants = Set.new
+    process = [self]
 
-          participants_for(value, current_user)
-        end.compact.uniq
-      end
+    until process.empty?
+      source = process.pop
 
-    unless Gitlab::ReferenceExtractor.lazy?
-      participants.select! do |user|
-        user.can?(:read_project, project)
+      case source
+      when User
+        participants << source
+      when Participable
+        source.class.participant_attrs.each do |attr|
+          if attr.respond_to?(:call)
+            source.instance_exec(current_user, ext, &attr)
+          else
+            process << source.__send__(attr)
+          end
+        end
+      when Enumerable, ActiveRecord::Relation
+        # This uses reverse_each so we can use "pop" to get the next value to
+        # process (in order). Using unshift instead of pop would require
+        # moving all Array values one index to the left (which can be
+        # expensive).
+        source.reverse_each { |obj| process << obj }
       end
     end
 
-    participants
-  end
-
-  private
+    participants.merge(ext.users)
 
-  def participants_for(value, current_user = nil)
-    case value
-    when User, Banzai::LazyReference
-      [value]
-    when Enumerable, ActiveRecord::Relation
-      value.flat_map { |v| participants_for(v, current_user) }
-    when Participable
-      value.participants(current_user)
-    end
+    Ability.users_that_can_read_project(participants.to_a, project)
   end
 end
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e498ca96e3c4b0f33922571910a5672697630b60
--- /dev/null
+++ b/app/models/deployment.rb
@@ -0,0 +1,29 @@
+class Deployment < ActiveRecord::Base
+  include InternalId
+
+  belongs_to :project, required: true, validate: true
+  belongs_to :environment, required: true, validate: true
+  belongs_to :user
+  belongs_to :deployable, polymorphic: true
+
+  validates :sha, presence: true
+  validates :ref, presence: true
+
+  delegate :name, to: :environment, prefix: true
+
+  def commit
+    project.commit(sha)
+  end
+
+  def commit_title
+    commit.try(:title)
+  end
+
+  def short_sha
+    Commit.truncate_sha(sha)
+  end
+
+  def last?
+    self == environment.last_deployment
+  end
+end
diff --git a/app/models/environment.rb b/app/models/environment.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ac3a571a1f3aa12f1a9209de40e124190354c788
--- /dev/null
+++ b/app/models/environment.rb
@@ -0,0 +1,16 @@
+class Environment < ActiveRecord::Base
+  belongs_to :project, required: true, validate: true
+
+  has_many :deployments
+
+  validates :name,
+            presence: true,
+            uniqueness: { scope: :project_id },
+            length: { within: 0..255 },
+            format: { with: Gitlab::Regex.environment_name_regex,
+                      message: Gitlab::Regex.environment_name_regex_message }
+
+  def last_deployment
+    deployments.last
+  end
+end
diff --git a/app/models/geo_node.rb b/app/models/geo_node.rb
index 82eea7d47717d549c56fc8c253e412ba90454f53..9acc3b27f799b9e860581eed498ba232b35b33e5 100644
--- a/app/models/geo_node.rb
+++ b/app/models/geo_node.rb
@@ -66,13 +66,11 @@ class GeoNode < ActiveRecord::Base
   end
 
   def oauth_callback_url
-    URI.join(uri, "#{uri.path}/", 'oauth/geo/callback').to_s
+    Gitlab::Routing.url_helpers.oauth_geo_callback_url(url_helper_args)
   end
 
   def oauth_logout_url(state)
-    logout_uri = URI.join(uri, "#{uri.path}/", 'oauth/geo/logout')
-    logout_uri.query = "state=#{state}"
-    logout_uri.to_s
+    Gitlab::Routing.url_helpers.oauth_geo_logout_url(url_helper_args.merge(state: state))
   end
 
   def missing_oauth_application?
@@ -81,6 +79,14 @@ class GeoNode < ActiveRecord::Base
 
   private
 
+  def url_helper_args
+    if relative_url_root
+      relative_url = relative_url_root.starts_with?('/') ? relative_url_root : "/#{relative_url_root}"
+    end
+
+    { protocol: schema, host: host, port: port, script_name: relative_url }
+  end
+
   def refresh_bulk_notify_worker_status
     if Gitlab::Geo.primary?
       Gitlab::Geo.bulk_notify_job.try(:enable!)
diff --git a/app/models/group.rb b/app/models/group.rb
index eb5c666554627baf0f7b26f0fb9c03eb3541cae0..8a136590a64a62b5dd258d6d4026d85b210e1136 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -3,11 +3,12 @@ require 'carrierwave/orm/activerecord'
 class Group < Namespace
   include Gitlab::ConfigHelper
   include Gitlab::VisibilityLevel
+  include AccessRequestable
   include Referable
 
   has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember'
   alias_method :members, :group_members
-  has_many :users, through: :group_members
+  has_many :users, -> { where(members: { requested_at: nil }) }, through: :group_members
   has_many :project_group_links, dependent: :destroy
   has_many :shared_projects, through: :project_group_links, source: :project
   has_many :ldap_group_links, foreign_key: 'group_id', dependent: :destroy
@@ -67,6 +68,10 @@ class Group < Namespace
     "#{self.class.reference_prefix}#{name}"
   end
 
+  def web_url
+    Gitlab::Routing.url_helpers.group_url(self)
+  end
+
   def human_name
     name
   end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 1ec113571edabbe2c75449b6fd4448674c2786b5..065febbf69f9940fbaa71bea795c7ea65a9b7d40 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -57,10 +57,18 @@ class Issue < ActiveRecord::Base
   end
 
   def self.visible_to_user(user)
-    return where(confidential: false) if user.blank?
+    return where('issues.confidential IS NULL OR issues.confidential IS FALSE') if user.blank?
     return all if user.admin?
 
-    where('issues.confidential = false OR (issues.confidential = true AND (issues.author_id = :user_id OR issues.assignee_id = :user_id OR issues.project_id IN(:project_ids)))', user_id: user.id, project_ids: user.authorized_projects.select(:id))
+    where('
+      issues.confidential IS NULL
+      OR issues.confidential IS FALSE
+      OR (issues.confidential = TRUE
+        AND (issues.author_id = :user_id
+          OR issues.assignee_id = :user_id
+          OR issues.project_id IN(:project_ids)))',
+      user_id: user.id,
+      project_ids: user.authorized_projects(Gitlab::Access::REPORTER).select(:id))
   end
 
   def self.reference_prefix
@@ -81,10 +89,10 @@ class Issue < ActiveRecord::Base
     @link_reference_pattern ||= super("issues", /(?<issue>\d+)/)
   end
 
-  def self.sort(method)
+  def self.sort(method, excluded_labels: [])
     case method.to_s
     when 'due_date_asc' then order_due_date_asc
-    when 'due_date_desc'  then order_due_date_desc
+    when 'due_date_desc' then order_due_date_desc
     else
       super
     end
@@ -101,14 +109,13 @@ class Issue < ActiveRecord::Base
   end
 
   def referenced_merge_requests(current_user = nil)
-    @referenced_merge_requests ||= {}
-    @referenced_merge_requests[current_user] ||= begin
-      Gitlab::ReferenceExtractor.lazily do
-        [self, *notes].flat_map do |note|
-          note.all_references(current_user).merge_requests
-        end
-      end.sort_by(&:iid).uniq
+    ext = all_references(current_user)
+
+    notes_with_associations.each do |object|
+      object.all_references(current_user, extractor: ext)
     end
+
+    ext.merge_requests.sort_by(&:iid)
   end
 
   # All branches containing the current issue's ID, except for
@@ -145,9 +152,13 @@ class Issue < ActiveRecord::Base
   def closed_by_merge_requests(current_user = nil)
     return [] unless open?
 
-    notes.system.flat_map do |note|
-      note.all_references(current_user).merge_requests
-    end.uniq.select { |mr| mr.open? && mr.closes_issue?(self) }
+    ext = all_references(current_user)
+
+    notes.system.each do |note|
+      note.all_references(current_user, extractor: ext)
+    end
+
+    ext.merge_requests.select { |mr| mr.open? && mr.closes_issue?(self) }
   end
 
   def self.weight_options
diff --git a/app/models/key.rb b/app/models/key.rb
index 244cd709d1f3b1599a209f524dc00712a3461322..e330bcd428f3daf209d09062063988d9d7496a1e 100644
--- a/app/models/key.rb
+++ b/app/models/key.rb
@@ -28,7 +28,7 @@ class Key < ActiveRecord::Base
   end
 
   def publishable_key
-    #Removes anything beyond the keytype and key itself
+    # Removes anything beyond the keytype and key itself
     self.key.split[0..1].join(' ')
   end
 
diff --git a/app/models/label.rb b/app/models/label.rb
index e5ad11983bea142981bf7f428d73210683dcb7c0..49c352cc239b811a7f8e772c00b9047d4d75f0a6 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -26,10 +26,20 @@ class Label < ActiveRecord::Base
             format: { with: /\A[^&\?,]+\z/ },
             uniqueness: { scope: :project_id }
 
+  before_save :nullify_priority
+
   default_scope { order(title: :asc) }
 
   scope :templates, ->  { where(template: true) }
 
+  def self.prioritized
+    where.not(priority: nil).reorder(:priority, :title)
+  end
+
+  def self.unprioritized
+    where(priority: nil)
+  end
+
   alias_attribute :name, :title
 
   def self.reference_prefix
@@ -118,4 +128,8 @@ class Label < ActiveRecord::Base
       id
     end
   end
+
+  def nullify_priority
+    self.priority = nil if priority.blank?
+  end
 end
diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb
index bbefc911b29ca4a5f65e37e19d5be5b6481af311..95fd510eb3a7460d6546192f7140473487498bb1 100644
--- a/app/models/legacy_diff_note.rb
+++ b/app/models/legacy_diff_note.rb
@@ -110,6 +110,10 @@ class LegacyDiffNote < Note
     @active
   end
 
+  def award_emoji_supported?
+    false
+  end
+
   private
 
   def find_diff
diff --git a/app/models/license.rb b/app/models/license.rb
index a917985513459460159b9d9bffd96bc1d81d05ef..b788832416d75cb66cc52d25764c790a94e1f53d 100644
--- a/app/models/license.rb
+++ b/app/models/license.rb
@@ -92,22 +92,6 @@ class License < ActiveRecord::Base
     self.restrictions[:active_user_count]
   end
 
-  def current_user_count
-    Rails.cache.fetch("current_active_user_count", expires_in: 1.hour) do
-      User.active.count
-    end
-  end
-
-  def warn_upgrade_license_message?
-    restricted_user_count = user_count
-
-    return unless restricted_user_count
-
-    return false unless Time.now >= self.starts_at + 3.months
-
-    restricted_user_count && current_user_count > restricted_user_count
-  end
-
   private
 
   def reset_current
diff --git a/app/models/member.rb b/app/models/member.rb
index 53fd93c0e6f0fa24d13d3532e56771e03f61c445..6157d5f85fd58ef65b513ef1bbecddcc05b1951e 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -27,20 +27,28 @@ class Member < ActiveRecord::Base
       allow_nil: true
     }
 
-  scope :invite, -> { where(user_id: nil) }
-  scope :non_invite, -> { where("user_id IS NOT NULL") }
+  scope :invite, -> { where.not(invite_token: nil) }
+  scope :non_invite, -> { where(invite_token: nil) }
+  scope :request, -> { where.not(requested_at: nil) }
+  scope :non_request, -> { where(requested_at: nil) }
+  scope :non_pending, -> { non_request.non_invite }
+
   scope :guests, -> { where(access_level: GUEST) }
   scope :reporters, -> { where(access_level: REPORTER) }
   scope :developers, -> { where(access_level: DEVELOPER) }
   scope :masters,  -> { where(access_level: MASTER) }
   scope :owners,  -> { where(access_level: OWNER) }
+  scope :owners_and_masters,  -> { where(access_level: [OWNER, MASTER]) }
 
   before_validation :generate_invite_token, on: :create, if: -> (member) { member.invite_email.present? }
+
   after_create :send_invite, if: :invite?
-  after_create :create_notification_setting, unless: :invite?
-  after_create :post_create_hook, unless: :invite?
-  after_update :post_update_hook, unless: :invite?
-  after_destroy :post_destroy_hook, unless: :invite?
+  after_create :send_request, if: :request?
+  after_create :create_notification_setting, unless: :pending?
+  after_create :post_create_hook, unless: :pending?
+  after_update :post_update_hook, unless: :pending?
+  after_destroy :post_destroy_hook, unless: :pending?
+  after_destroy :post_decline_request, if: :request?
 
   delegate :name, :username, :email, to: :user, prefix: true
 
@@ -99,10 +107,31 @@ class Member < ActiveRecord::Base
     end
   end
 
+  def real_source_type
+    source_type
+  end
+
   def invite?
     self.invite_token.present?
   end
 
+  def request?
+    requested_at.present?
+  end
+
+  def pending?
+    invite? || request?
+  end
+
+  def accept_request
+    return false unless request?
+
+    updated = self.update(requested_at: nil)
+    after_accept_request if updated
+
+    updated
+  end
+
   def accept_invite!(new_user)
     return false unless invite?
 
@@ -160,6 +189,10 @@ class Member < ActiveRecord::Base
     # override in subclass
   end
 
+  def send_request
+    # override in subclass
+  end
+
   def post_create_hook
     system_hook_service.execute_hooks_for(self, :create)
   end
@@ -180,6 +213,14 @@ class Member < ActiveRecord::Base
     # override in subclass
   end
 
+  def after_accept_request
+    post_create_hook
+  end
+
+  def post_decline_request
+    # override in subclass
+  end
+
   def system_hook_service
     SystemHooksService.new
   end
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index 9a4f1e4042b70ee7e335d02300e8c53275ce327b..4c50cd2d8944e730e98f37daa45f387f7cb0862e 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -8,8 +8,6 @@ class GroupMember < Member
   validates_format_of :source_type, with: /\ANamespace\z/
   default_scope { where(source_type: SOURCE_TYPE) }
 
-  scope :with_group, ->(group) { where(source_id: group.id) }
-  scope :with_user, ->(user) { where(user_id: user.id) }
   scope :with_ldap_dn, -> { joins(user: :identities).where("identities.provider LIKE ?", 'ldap%') }
   scope :select_access_level_and_user, -> { select(:access_level, :user_id) }
   scope :with_identity_provider, ->(provider) do
@@ -28,6 +26,11 @@ class GroupMember < Member
     access_level
   end
 
+  # Because source_type is `Namespace`...
+  def real_source_type
+    'Group'
+  end
+
   private
 
   def send_invite
@@ -36,6 +39,18 @@ class GroupMember < Member
     super
   end
 
+  def send_request
+    notification_service.new_group_access_request(self)
+
+    super
+  end
+
+  def send_request
+    notification_service.new_group_access_request(self)
+
+    super
+  end
+
   def post_create_hook
     notification_service.new_group_member(self) unless @skip_notification
 
@@ -61,4 +76,16 @@ class GroupMember < Member
 
     super
   end
+
+  def post_decline_request
+    notification_service.decline_group_access_request(self)
+
+    super
+  end
+
+  def post_decline_request
+    notification_service.decline_group_access_request(self)
+
+    super
+  end
 end
diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb
index 8327ffa397eb9cacc6c03479a402923cae9f0b39..677f8ac4eb4a7480142ffba86ae50ab5395f2d3f 100644
--- a/app/models/members/project_member.rb
+++ b/app/models/members/project_member.rb
@@ -5,15 +5,14 @@ class ProjectMember < Member
 
   belongs_to :project, class_name: 'Project', foreign_key: 'source_id'
 
-
   # Make sure project member points only to project as it source
   default_value_for :source_type, SOURCE_TYPE
   validates_format_of :source_type, with: /\AProject\z/
   default_scope { where(source_type: SOURCE_TYPE) }
 
   scope :in_project, ->(project) { where(source_id: project.id) }
-  scope :in_projects, ->(projects) { where(source_id: projects.pluck(:id)) }
-  scope :with_user, ->(user) { where(user_id: user.id) }
+
+  before_destroy :delete_member_todos
 
   class << self
 
@@ -83,7 +82,7 @@ class ProjectMember < Member
       Gitlab::Access.sym_options
     end
 
-    def access_roles
+    def access_level_roles
       Gitlab::Access.options
     end
   end
@@ -102,12 +101,28 @@ class ProjectMember < Member
 
   private
 
+  def delete_member_todos
+    user.todos.where(project_id: source_id).destroy_all if user
+  end
+
   def send_invite
     notification_service.invite_project_member(self, @raw_invite_token) unless @skip_notification
 
     super
   end
 
+  def send_request
+    notification_service.new_project_access_request(self)
+
+    super
+  end
+
+  def send_request
+    notification_service.new_project_access_request(self)
+
+    super
+  end
+
   def post_create_hook
     unless owner?
       event_service.join_project(self.project, self.user)
@@ -143,6 +158,18 @@ class ProjectMember < Member
     super
   end
 
+  def post_decline_request
+    notification_service.decline_project_access_request(self)
+
+    super
+  end
+
+  def post_decline_request
+    notification_service.decline_project_access_request(self)
+
+    super
+  end
+
   def event_service
     EventCreateService.new
   end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index d89f76f6bb8b6b65fda7c25735ba647c4f7a1b1a..11517e5412fa8cb7899ac0a6bfb4de3fb8f7f5a0 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -264,19 +264,20 @@ class MergeRequest < ActiveRecord::Base
   end
 
   def mergeable?
-    return false unless open? && !work_in_progress? && !broken?
+    return false unless mergeable_state?
 
     check_if_can_be_merged
 
     can_be_merged? && !must_be_rebased?
   end
 
-  def gitlab_merge_status
-    if work_in_progress?
-      "work_in_progress"
-    else
-      merge_status_name
-    end
+  def mergeable_state?
+    return false unless open?
+    return false if work_in_progress?
+    return false if broken?
+    return false unless mergeable_ci_state?
+
+    true
   end
 
   def can_cancel_merge_when_build_succeeds?(current_user)
@@ -317,13 +318,6 @@ class MergeRequest < ActiveRecord::Base
     )
   end
 
-  # Returns the raw diff for this merge request
-  #
-  # see "git diff"
-  def to_diff
-    target_project.repository.diff_text(diff_base_commit.sha, source_sha)
-  end
-
   # Returns the commit as a series of email patches.
   #
   # see "git format-patch"
@@ -481,8 +475,7 @@ class MergeRequest < ActiveRecord::Base
   end
 
   def approvers_left
-    user_ids = overall_approvers.map(&:user_id) - approvals.map(&:user_id)
-    User.where id: user_ids
+    User.where(id: overall_approvers.select(:user_id)).where.not(id: approvals.select(:user_id))
   end
 
   def approvals_required
@@ -514,8 +507,10 @@ class MergeRequest < ActiveRecord::Base
   end
 
   def can_approve?(user)
+    return false if user == self.author
+
     approvers_left.include?(user) ||
-    (any_approver_allowed? && !approved_by?(user))
+      (any_approver_allowed? && !approved_by?(user))
   end
 
   def any_approver_allowed?
@@ -538,6 +533,12 @@ class MergeRequest < ActiveRecord::Base
     ::Gitlab::GitAccess.new(user, project).can_push_to_branch?(target_branch)
   end
 
+  def mergeable_ci_state?
+    return true unless project.only_allow_merge_if_build_succeeds?
+
+    !pipeline || pipeline.success?
+  end
+
   def state_human_name
     if merged?
       "Merged"
@@ -610,7 +611,7 @@ class MergeRequest < ActiveRecord::Base
   end
 
   def source_sha_parent
-    source_project.repository.commit(commits.last.sha).parents.first.sha
+    source_project.repository.commit(first_commit.sha).parents.first.sha
   end
 
   def ff_merge_possible?
@@ -655,8 +656,8 @@ class MergeRequest < ActiveRecord::Base
     diverged_commits_count > 0
   end
 
-  def ci_commit
-    @ci_commit ||= source_project.ci_commit(last_commit.id, source_branch) if last_commit && source_project
+  def pipeline
+    @pipeline ||= source_project.pipeline(last_commit.id, source_branch) if last_commit && source_project
   end
 
   def diff_refs
diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb
index f4e90125373dba51119c47fc2c822798e22400d5..a2aee2f925bca3f13769243b312d25168cdcee9c 100644
--- a/app/models/network/graph.rb
+++ b/app/models/network/graph.rb
@@ -22,9 +22,16 @@ module Network
 
     def collect_notes
       h = Hash.new(0)
-      @project.notes.where('noteable_type = ?' ,"Commit").group('notes.commit_id').select('notes.commit_id, count(notes.id) as note_count').each do |item|
-        h[item.commit_id] = item.note_count.to_i
-      end
+
+      @project
+        .notes
+        .where('noteable_type = ?', 'Commit')
+        .group('notes.commit_id')
+        .select('notes.commit_id, count(notes.id) as note_count')
+        .each do |item|
+          h[item.commit_id] = item.note_count.to_i
+        end
+
       h
     end
 
@@ -89,7 +96,7 @@ module Network
         end
       end
 
-      if self.class.max_count / 2 < offset then
+      if self.class.max_count / 2 < offset
         # get max index that commit is displayed in the center.
         offset - self.class.max_count / 2
       else
@@ -130,7 +137,7 @@ module Network
       commit.parents(@map).each do |parent|
         range = commit.time..parent.time
 
-        space = if commit.space >= parent.space then
+        space = if commit.space >= parent.space
                   find_free_parent_space(range, parent.space, -1, commit.space)
                 else
                   find_free_parent_space(range, commit.space, -1, parent.space)
@@ -144,7 +151,7 @@ module Network
     end
 
     def find_free_parent_space(range, space_base, space_step, space_default)
-      if is_overlap?(range, space_default) then
+      if is_overlap?(range, space_default)
         find_free_space(range, space_step, space_base, space_default)
       else
         space_default
@@ -155,9 +162,9 @@ module Network
       range.each do |i|
         if i != range.first &&
           i != range.last &&
-          @commits[i].spaces.include?(overlap_space) then
+          @commits[i].spaces.include?(overlap_space)
 
-          return true;
+          return true
         end
       end
 
@@ -198,7 +205,7 @@ module Network
       # Visit branching chains
       leaves.each do |l|
         parents = l.parents(@map).select{|p| p.space.zero?}
-        for p in parents
+        parents.each do |p|
           place_chain(p, l.time)
         end
       end
@@ -216,7 +223,7 @@ module Network
     end
 
     def mark_reserved(time_range, space)
-      for day in time_range
+      time_range.each do |day|
         @reserved[day].push(space)
       end
     end
@@ -225,15 +232,15 @@ module Network
       space_default ||= space_base
 
       reserved = []
-      for day in time_range
+      time_range.each do |day|
         reserved.push(*@reserved[day])
       end
       reserved.uniq!
 
       space = space_default
-      while reserved.include?(space) do
+      while reserved.include?(space)
         space += space_step
-        if space < space_base then
+        if space < space_base
           space_step *= -1
           space = space_base + space_step
         end
@@ -253,7 +260,7 @@ module Network
       leaves = []
       leaves.push(commit) if commit.space.zero?
 
-      while true
+      loop do
         return leaves if commit.parents(@map).count.zero?
 
         commit = commit.parents(@map).first
diff --git a/app/models/note.rb b/app/models/note.rb
index 7f872af277a5cf3a7f2bc841d775f2b6ebeaf924..f0fa8ff0abb6c40ff3899530deabfba1ba48c2f4 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -4,10 +4,11 @@ class Note < ActiveRecord::Base
   include Participable
   include Mentionable
   include Elastic::NotesSearch
+  include Awardable
 
   default_value_for :system, false
 
-  attr_mentionable :note, cache: true, pipeline: :note
+  attr_mentionable :note, pipeline: :note
   participant :author
 
   belongs_to :project
@@ -22,24 +23,26 @@ class Note < ActiveRecord::Base
   delegate :name, :email, to: :author, prefix: true
   delegate :title, to: :noteable, allow_nil: true
 
-  before_validation :set_award!
-
   validates :note, :project, presence: true
-  validates :note, uniqueness: { scope: [:author, :noteable_type, :noteable_id] }, if: ->(n) { n.is_award }
-  validates :note, inclusion: { in: Emoji.emojis_names }, if: ->(n) { n.is_award }
+
   # Attachments are deprecated and are handled by Markdown uploader
   validates :attachment, file_size: { maximum: :max_attachment_size }
 
-  validates :noteable_id, presence: true, if: ->(n) { n.noteable_type.present? && n.noteable_type != 'Commit' }
-  validates :commit_id, presence: true, if: ->(n) { n.noteable_type == 'Commit' }
+  validates :noteable_type, presence: true
+  validates :noteable_id, presence: true, unless: :for_commit?
+  validates :commit_id, presence: true, if: :for_commit?
   validates :author, presence: true
 
+  validate unless: :for_commit? do |note|
+    unless note.noteable.try(:project) == note.project
+      errors.add(:invalid_project, 'Note and noteable project mismatch')
+    end
+  end
+
   mount_uploader :attachment, AttachmentUploader
 
   # Scopes
-  scope :awards, ->{ where(is_award: true) }
-  scope :nonawards, ->{ where(is_award: false) }
-  scope :searchable, ->{ where("is_award IS FALSE AND system IS FALSE") }
+  scope :searchable, ->{ where(system: false) }
   scope :for_commit_id, ->(commit_id) { where(noteable_type: "Commit", commit_id: commit_id) }
   scope :system, ->{ where(system: true) }
   scope :user, ->{ where(system: false) }
@@ -79,32 +82,22 @@ class Note < ActiveRecord::Base
     #
     # This method uses ILIKE on PostgreSQL and LIKE on MySQL.
     #
-    # query - The search query as a String.
+    # query   - The search query as a String.
+    # as_user - Limit results to those viewable by a specific user
     #
     # Returns an ActiveRecord::Relation.
-    def search(query)
+    def search(query, as_user: nil)
       table   = arel_table
       pattern = "%#{query}%"
 
-      where(table[:note].matches(pattern))
-    end
-
-    def grouped_awards
-      notes = {}
-
-      awards.select(:note).distinct.map do |note|
-        notes[note.note] = where(note: note.note)
-      end
-
-      notes["thumbsup"] ||= Note.none
-      notes["thumbsdown"] ||= Note.none
-
-      notes
+      Note.joins('LEFT JOIN issues ON issues.id = noteable_id').
+        where(table[:note].matches(pattern)).
+        merge(Issue.visible_to_user(as_user))
     end
   end
 
   def searchable?
-    !is_award && !system
+    !system
   end
 
   def cross_reference?
@@ -188,44 +181,28 @@ class Note < ActiveRecord::Base
     Event.reset_event_cache_for(self)
   end
 
-  def downvote?
-    is_award && note == "thumbsdown"
-  end
-
-  def upvote?
-    is_award && note == "thumbsup"
-  end
-
   def editable?
-    !system? && !is_award
+    !system?
   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
-  #   and changes content of the note to award name.
-  #
-  # Method is executed as a before_validation callback.
-  #
-  def set_award!
-    return unless awards_supported? && contains_emoji_only?
-
-    self.is_award = true
-    self.note = award_emoji_name
+  def award_emoji?
+    award_emoji_supported? && contains_emoji_only?
   end
 
-  private
+  def emoji_awardable?
+    !system?
+  end
 
   def clear_blank_line_code!
     self.line_code = nil if self.line_code.blank?
   end
 
-  def awards_supported?
-    (for_issue? || for_merge_request?) && !diff_note?
+  def award_emoji_supported?
+    noteable.is_a?(Awardable)
   end
 
   def contains_emoji_only?
@@ -234,6 +211,6 @@ class Note < ActiveRecord::Base
 
   def award_emoji_name
     original_name = note.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1]
-    AwardEmoji.normilize_emoji_name(original_name)
+    Gitlab::AwardEmoji.normalize_emoji_name(original_name)
   end
 end
diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb
index 5001738f411f43a05c678d7edf6d0fd85bbda8d6..0ce87968e46bfc7a37d975fb294255f0071ddd87 100644
--- a/app/models/notification_setting.rb
+++ b/app/models/notification_setting.rb
@@ -1,5 +1,5 @@
 class NotificationSetting < ActiveRecord::Base
-  enum level: { disabled: 0,  participating: 1,  watch: 2,  global: 3, mention: 4 }
+  enum level: { global: 3, watch: 2, mention: 4, participating: 1, disabled: 0 }
 
   default_value_for :level, NotificationSetting.levels[:global]
 
@@ -7,7 +7,6 @@ class NotificationSetting < ActiveRecord::Base
   belongs_to :source, polymorphic: true
 
   validates :user, presence: true
-  validates :source, presence: true
   validates :level, presence: true
   validates :user_id, uniqueness: { scope: [:source_type, :source_id],
                                     message: "already exists in source",
diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb
index 9155e57331d7e63f9320e6885d2e35eeeb1927d6..2f4cded15c81ea1b38c0ed59b6dc6ee286d10a9a 100644
--- a/app/models/pages_domain.rb
+++ b/app/models/pages_domain.rb
@@ -10,7 +10,10 @@ class PagesDomain < ActiveRecord::Base
   validate :validate_matching_key, if: ->(domain) { domain.certificate.present? || domain.key.present? }
   validate :validate_intermediates, if: ->(domain) { domain.certificate.present? }
 
-  attr_encrypted :key, mode: :per_attribute_iv_and_salt, key: Gitlab::Application.secrets.db_key_base
+  attr_encrypted :key,
+    mode: :per_attribute_iv_and_salt,
+    key: Gitlab::Application.secrets.db_key_base,
+    algorithm: 'aes-256-cbc'
 
   after_create :update
   after_save :update
diff --git a/app/models/path_lock.rb b/app/models/path_lock.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fa22411cfc01d5f444480e1406b005813cb10a9f
--- /dev/null
+++ b/app/models/path_lock.rb
@@ -0,0 +1,8 @@
+class PathLock < ActiveRecord::Base
+  belongs_to :project
+  belongs_to :user
+
+  validates :project, presence: true
+  validates :user, presence: true
+  validates :path, presence: true, uniqueness: { scope: [:user, :project] }
+end
diff --git a/app/models/project.rb b/app/models/project.rb
index 253e9cbfb5e64c197299c7a8646cd5f2790066aa..36dfa1a985f5ba9af4918bb392b3e906b6365038 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -5,6 +5,7 @@ class Project < ActiveRecord::Base
   include Gitlab::ShellAdapter
   include Gitlab::VisibilityLevel
   include Gitlab::CurrentSettings
+  include AccessRequestable
   include Referable
   include Sortable
   include AfterCommitQueue
@@ -78,7 +79,7 @@ class Project < ActiveRecord::Base
   has_one :jira_service, dependent: :destroy
   has_one :redmine_service, dependent: :destroy
   has_one :custom_issue_tracker_service, dependent: :destroy
-  has_one :gitlab_issue_tracker_service, dependent: :destroy
+  has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project
   has_one :external_wiki_service, dependent: :destroy
   has_one :index_status, dependent: :destroy
 
@@ -101,8 +102,9 @@ class Project < ActiveRecord::Base
   has_many :snippets,           dependent: :destroy, class_name: 'ProjectSnippet'
   has_many :hooks,              dependent: :destroy, class_name: 'ProjectHook'
   has_many :protected_branches, dependent: :destroy
-  has_many :project_members, dependent: :destroy, as: :source, class_name: 'ProjectMember'
-  has_many :users, through: :project_members
+  has_many :project_members,    dependent: :destroy, as: :source, class_name: 'ProjectMember'
+  alias_method :members, :project_members
+  has_many :users, -> { where(members: { requested_at: nil }) }, through: :project_members
   has_many :deploy_keys_projects, dependent: :destroy
   has_many :deploy_keys, through: :deploy_keys_projects
   has_many :users_star_projects, dependent: :destroy
@@ -121,13 +123,16 @@ class Project < ActiveRecord::Base
   has_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
 
   has_many :commit_statuses, dependent: :destroy, class_name: 'CommitStatus', foreign_key: :gl_project_id
-  has_many :ci_commits, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id
+  has_many :pipelines, dependent: :destroy, class_name: 'Ci::Pipeline', foreign_key: :gl_project_id
   has_many :builds, class_name: 'Ci::Build', foreign_key: :gl_project_id # the builds are created from the commit_statuses
   has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject', foreign_key: :gl_project_id
   has_many :runners, through: :runner_projects, source: :runner, class_name: 'Ci::Runner'
   has_many :variables, dependent: :destroy, class_name: 'Ci::Variable', foreign_key: :gl_project_id
   has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger', foreign_key: :gl_project_id
   has_many :remote_mirrors, dependent: :destroy
+  has_many :environments, dependent: :destroy
+  has_many :deployments, dependent: :destroy
+  has_many :path_locks, dependent: :destroy
 
   accepts_nested_attributes_for :variables, allow_destroy: true
   accepts_nested_attributes_for :remote_mirrors,
@@ -151,7 +156,6 @@ class Project < ActiveRecord::Base
               message: Gitlab::Regex.project_path_regex_message }
   validates :issues_enabled, :merge_requests_enabled,
             :wiki_enabled, inclusion: { in: [true, false] }
-  validates :issues_tracker_id, length: { maximum: 255 }, allow_blank: true
   validates :namespace, presence: true
   validates_uniqueness_of :name, scope: :namespace_id
   validates_uniqueness_of :path, scope: :namespace_id
@@ -226,7 +230,7 @@ class Project < ActiveRecord::Base
         project.save
       end
 
-      if Gitlab.config.elasticsearch.enabled
+      if current_application_settings.elasticsearch_indexing?
         project.repository.index_blobs
         project.repository.index_commits
       end
@@ -285,20 +289,69 @@ class Project < ActiveRecord::Base
       non_archived.where(table[:name].matches(pattern))
     end
 
-    def find_with_namespace(id)
-      namespace_path, project_path = id.split('/', 2)
+    # Finds a single project for the given path.
+    #
+    # path - The full project path (including namespace path).
+    #
+    # Returns a Project, or nil if no project could be found.
+    def find_with_namespace(path)
+      where_paths_in([path]).reorder(nil).take
+    end
 
-      return nil if !namespace_path || !project_path
+    # Builds a relation to find multiple projects by their full paths.
+    #
+    # Each path must be in the following format:
+    #
+    #     namespace_path/project_path
+    #
+    # For example:
+    #
+    #     gitlab-org/gitlab-ce
+    #
+    # Usage:
+    #
+    #     Project.where_paths_in(%w{gitlab-org/gitlab-ce gitlab-org/gitlab-ee})
+    #
+    # This would return the projects with the full paths matching the values
+    # given.
+    #
+    # paths - An Array of full paths (namespace path + project path) for which
+    #         to find the projects.
+    #
+    # Returns an ActiveRecord::Relation.
+    def where_paths_in(paths)
+      wheres = []
+      cast_lower = Gitlab::Database.postgresql?
+
+      paths.each do |path|
+        namespace_path, project_path = path.split('/', 2)
+
+        next unless namespace_path && project_path
+
+        namespace_path = connection.quote(namespace_path)
+        project_path = connection.quote(project_path)
+
+        where = "(namespaces.path = #{namespace_path}
+          AND projects.path = #{project_path})"
+
+        if cast_lower
+          where = "(
+            #{where}
+            OR (
+              LOWER(namespaces.path) = LOWER(#{namespace_path})
+              AND LOWER(projects.path) = LOWER(#{project_path})
+            )
+          )"
+        end
 
-      # Use of unscoped ensures we're not secretly adding any ORDER BYs, which
-      # have a negative impact on performance (and aren't needed for this
-      # query).
-      projects = unscoped.
-        joins(:namespace).
-        iwhere('namespaces.path' => namespace_path)
+        wheres << where
+      end
 
-      projects.find_by('projects.path' => project_path) ||
-        projects.iwhere('projects.path' => project_path).take
+      if wheres.empty?
+        none
+      else
+        joins(:namespace).where(wheres.join(' OR '))
+      end
     end
 
     def visibility_levels
@@ -341,21 +394,25 @@ class Project < ActiveRecord::Base
     @repository ||= Repository.new(path_with_namespace, self)
   end
 
+  def container_registry_path_with_namespace
+    path_with_namespace.downcase
+  end
+
   def container_registry_repository
     return unless Gitlab.config.registry.enabled
 
     @container_registry_repository ||= begin
-      token = Auth::ContainerRegistryAuthenticationService.full_access_token(path_with_namespace)
+      token = Auth::ContainerRegistryAuthenticationService.full_access_token(container_registry_path_with_namespace)
       url = Gitlab.config.registry.api_url
       host_port = Gitlab.config.registry.host_port
       registry = ContainerRegistry::Registry.new(url, token: token, path: host_port)
-      registry.repository(path_with_namespace)
+      registry.repository(container_registry_path_with_namespace)
     end
   end
 
   def container_registry_repository_url
     if Gitlab.config.registry.enabled
-      "#{Gitlab.config.registry.host_port}/#{path_with_namespace}"
+      "#{Gitlab.config.registry.host_port}/#{container_registry_path_with_namespace}"
     end
   end
 
@@ -491,7 +548,7 @@ class Project < ActiveRecord::Base
     mirror_updated? && self.mirror_last_successful_update_at
   end
 
-  def update_mirror
+  def update_mirror(delay: 0)
     return unless mirror? && repository_exists?
 
     return if import_in_progress?
@@ -502,7 +559,7 @@ class Project < ActiveRecord::Base
       import_start
     end
 
-    RepositoryUpdateMirrorWorker.perform_async(self.id)
+    RepositoryUpdateMirrorWorker.perform_in(delay, self.id)
   end
 
   def mark_import_as_failed(error_message)
@@ -530,7 +587,13 @@ class Project < ActiveRecord::Base
 
   def check_limit
     unless creator.can_create_project? or namespace.kind == 'group'
-      self.errors.add(:limit_reached, "Your project limit is #{creator.projects_limit} projects! Please contact your administrator to increase it")
+      projects_limit = creator.projects_limit
+
+      if projects_limit == 0
+        self.errors.add(:limit_reached, "Personal project creation is not allowed. Please contact your administrator with questions")
+      else
+        self.errors.add(:limit_reached, "Your project limit is #{projects_limit} projects! Please contact your administrator to increase it")
+      end
     end
   rescue
     self.errors.add(:base, "Can't check your ability to create project")
@@ -612,13 +675,21 @@ class Project < ActiveRecord::Base
   end
 
   def external_issue_tracker
-    return @external_issue_tracker if defined?(@external_issue_tracker)
-    @external_issue_tracker ||=
-      services.issue_trackers.active.without_defaults.first
+    if has_external_issue_tracker.nil? # To populate existing projects
+      cache_has_external_issue_tracker
+    end
+
+    if has_external_issue_tracker?
+      return @external_issue_tracker if defined?(@external_issue_tracker)
+
+      @external_issue_tracker = services.external_issue_trackers.first
+    else
+      nil
+    end
   end
 
-  def can_have_issues_tracker_id?
-    self.issues_enabled && !self.default_issues_tracker?
+  def cache_has_external_issue_tracker
+    update_column(:has_external_issue_tracker, services.external_issue_trackers.any?)
   end
 
   def build_missing_services
@@ -717,16 +788,6 @@ class Project < ActiveRecord::Base
     end
   end
 
-  def project_member_by_name_or_email(name = nil, email = nil)
-    user = users.find_by('name like ? or email like ?', name, email)
-    project_members.where(user: user) if user
-  end
-
-  # Get Team Member record by user id
-  def project_member_by_id(user_id)
-    project_members.find_by(user_id: user_id)
-  end
-
   def name_with_namespace
     @name_with_namespace ||= begin
                                if namespace
@@ -736,6 +797,7 @@ class Project < ActiveRecord::Base
                                end
                              end
   end
+  alias_method :human_name, :name_with_namespace
 
   def path_with_namespace
     if namespace
@@ -1056,12 +1118,12 @@ class Project < ActiveRecord::Base
     !namespace.share_with_group_lock
   end
 
-  def ci_commit(sha, ref)
-    ci_commits.order(id: :desc).find_by(sha: sha, ref: ref)
+  def pipeline(sha, ref)
+    pipelines.order(id: :desc).find_by(sha: sha, ref: ref)
   end
 
-  def ensure_ci_commit(sha, ref)
-    ci_commit(sha, ref) || ci_commits.create(sha: sha, ref: ref)
+  def ensure_pipeline(sha, ref)
+    pipeline(sha, ref) || pipelines.create(sha: sha, ref: ref)
   end
 
   def enable_ci
@@ -1076,13 +1138,13 @@ class Project < ActiveRecord::Base
     shared_runners_enabled? && Ci::Runner.shared.active.any?(&block)
   end
 
-  def valid_runners_token? token
+  def valid_runners_token?(token)
     self.runners_token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.runners_token)
   end
 
   # TODO (ayufan): For now we use runners_token (backward compatibility)
   # In 8.4 every build will have its own individual token valid for time of build
-  def valid_build_token? token
+  def valid_build_token?(token)
     self.builds_enabled? && self.runners_token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.runners_token)
   end
 
@@ -1131,6 +1193,11 @@ class Project < ActiveRecord::Base
     Dir.exist?(public_pages_path)
   end
 
+  def path_lock_info(path, exact_match: false)
+    @path_lock_finder ||= Gitlab::PathLocksFinder.new(self)
+    @path_lock_finder.get_lock_info(path, exact_match: exact_match)
+  end
+
   def schedule_delete!(user_id, params)
     # Queue this task for after the commit, so once we mark pending_delete it will run
     run_after_commit { ProjectDestroyWorker.perform_async(id, user_id, params) }
@@ -1206,8 +1273,6 @@ class Project < ActiveRecord::Base
     import_url_changed? && changes['import_url'].first
   end
 
-  private
-
   def update_forks_visibility_level
     return unless visibility_level < visibility_level_was
 
@@ -1232,4 +1297,22 @@ class Project < ActiveRecord::Base
   def mark_remote_mirrors_for_removal
     remote_mirrors.each(&:mark_for_delete_if_blank_url)
   end
+
+  def running_or_pending_build_count(force: false)
+    Rails.cache.fetch(['projects', id, 'running_or_pending_build_count'], force: force) do
+      builds.running_or_pending.count(:all)
+    end
+  end
+
+  def mark_import_as_failed(error_message)
+    original_errors = errors.dup
+    sanitized_message = Gitlab::UrlSanitizer.sanitize(error_message)
+
+    import_fail
+    update_column(:import_error, sanitized_message)
+  rescue ActiveRecord::ActiveRecordError => e
+    Rails.logger.error("Error setting import status to failed: #{e.message}. Original error: #{sanitized_message}")
+  ensure
+    @errors = original_errors
+  end
 end
diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb
index e2f9ffb69acb98db7becc0adff22018c12e69293..ca8a9b4217b6e5334cf52d8f0bec6fd9047a5190 100644
--- a/app/models/project_import_data.rb
+++ b/app/models/project_import_data.rb
@@ -6,7 +6,8 @@ class ProjectImportData < ActiveRecord::Base
                  key: Gitlab::Application.secrets.db_key_base,
                  marshal: true,
                  encode: true,
-                 mode: :per_attribute_iv_and_salt
+                 mode: :per_attribute_iv_and_salt,
+                 algorithm: 'aes-256-cbc'
 
   serialize :data, JSON
 
diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb
index 1d1780dcfbf3e221f8d8cb22988b79774605af7b..b5c76e4d4fef5ca93a219b731f3765fd71d568d9 100644
--- a/app/models/project_services/bamboo_service.rb
+++ b/app/models/project_services/bamboo_service.rb
@@ -1,6 +1,4 @@
 class BambooService < CiService
-  include HTTParty
-
   prop_accessor :bamboo_url, :build_key, :username, :password
 
   validates :bamboo_url, presence: true, url: true, if: :activated?
@@ -61,18 +59,7 @@ class BambooService < CiService
   end
 
   def build_info(sha)
-    url = URI.join(bamboo_url, "/rest/api/latest/result?label=#{sha}").to_s
-
-    if username.blank? && password.blank?
-      @response = HTTParty.get(url, verify: false)
-    else
-      url << '&os_authType=basic'
-      auth = {
-        username: username,
-        password: password
-      }
-      @response = HTTParty.get(url, verify: false, basic_auth: auth)
-    end
+    @response = get_path("rest/api/latest/result?label=#{sha}")
   end
 
   def build_page(sha, ref)
@@ -80,11 +67,11 @@ class BambooService < CiService
 
     if @response.code != 200 || @response['results']['results']['size'] == '0'
       # If actual build link can't be determined, send user to build summary page.
-      URI.join(bamboo_url, "/browse/#{build_key}").to_s
+      URI.join("#{bamboo_url}/", "browse/#{build_key}").to_s
     else
       # If actual build link is available, go to build result page.
       result_key = @response['results']['results']['result']['planResultKey']['key']
-      URI.join(bamboo_url, "/browse/#{result_key}").to_s
+      URI.join("#{bamboo_url}/", "browse/#{result_key}").to_s
     end
   end
 
@@ -112,8 +99,27 @@ class BambooService < CiService
   def execute(data)
     return unless supported_events.include?(data[:object_kind])
 
-    # Bamboo requires a GET and does not take any data.
-    url = URI.join(bamboo_url, "/updateAndBuild.action?buildKey=#{build_key}").to_s
-    self.class.get(url, verify: false)
+    get_path("updateAndBuild.action?buildKey=#{build_key}")
+  end
+
+  private
+
+  def build_url(path)
+    URI.join("#{bamboo_url}/", path).to_s
+  end
+
+  def get_path(path)
+    url = build_url(path)
+
+    if username.blank? && password.blank?
+      HTTParty.get(url, verify: false)
+    else
+      url << '&os_authType=basic'
+      HTTParty.get(url, verify: false,
+                        basic_auth: {
+                          username: username,
+                          password: password
+                        })
+    end
   end
 end
diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb
index 91015e6c9b1404886eea77cc7f15883f26d906e0..58cb720c3c10dd3c51723eec37bf2b095c1f517a 100644
--- a/app/models/project_services/irker_service.rb
+++ b/app/models/project_services/irker_service.rb
@@ -70,7 +70,7 @@ class IrkerService < Service
   private
 
   def get_channels
-    return true unless :activated?
+    return true unless activated?
     return true if recipients.nil? || recipients.empty?
 
     map_recipients
@@ -83,7 +83,7 @@ class IrkerService < Service
     self.channels = recipients.split(/\s+/).map do |recipient|
       format_channel(recipient)
     end
-    channels.reject! &:nil?
+    channels.reject!(&:nil?)
   end
 
   def format_channel(recipient)
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index 37d9f83473c2c53eec5b49115a56e5267aa02a53..cb4248f7dc16b80950856137518819c83afb6d8c 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -42,9 +42,9 @@ class IssueTrackerService < Service
       if enabled_in_gitlab_config
         self.properties = {
           title: issues_tracker['title'],
-          project_url: add_issues_tracker_id(issues_tracker['project_url']),
-          issues_url: add_issues_tracker_id(issues_tracker['issues_url']),
-          new_issue_url: add_issues_tracker_id(issues_tracker['new_issue_url'])
+          project_url: issues_tracker['project_url'],
+          issues_url: issues_tracker['issues_url'],
+          new_issue_url: issues_tracker['new_issue_url']
         }
       else
         self.properties = {}
@@ -87,16 +87,4 @@ class IssueTrackerService < Service
   def issues_tracker
     Gitlab.config.issues_tracker[to_param]
   end
-
-  def add_issues_tracker_id(url)
-    if self.project
-      id = self.project.issues_tracker_id
-
-      if id
-        url = url.gsub(":issues_tracker_id", id)
-      end
-    end
-
-    url
-  end
 end
diff --git a/app/models/project_services/jenkins_service.rb b/app/models/project_services/jenkins_service.rb
index 141e91fba75a50317c00f70a9df298612215c77b..07add72ca9f18ea65f3a58f0d660580a8ba41a8b 100644
--- a/app/models/project_services/jenkins_service.rb
+++ b/app/models/project_services/jenkins_service.rb
@@ -53,14 +53,13 @@ class JenkinsService < CiService
 
   def test(data)
     begin
-      result = execute(data)
-      message = result.message || result unless result.nil?
-      return { success: false, result: message } if result.code != 200
+      code, message = execute(data)
+      return { success: false, result: message } if code != 200
     rescue StandardError => error
       return { success: false, result: error }
     end
 
-    { success: true, result: result }
+    { success: true, result: message }
   end
 
   def auth
diff --git a/app/models/project_services/slack_service/build_message.rb b/app/models/project_services/slack_service/build_message.rb
index c124cad4afd04208dbbcd72ccbf5ee468ef6e4d9..69c21b3fc387fdf4ecbf98773ebf468ed8812f87 100644
--- a/app/models/project_services/slack_service/build_message.rb
+++ b/app/models/project_services/slack_service/build_message.rb
@@ -35,8 +35,8 @@ class SlackService
     private
 
     def message
-      "#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status} in #{duration} second(s)"
-    end
+      "#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status} in #{duration} #{'second'.pluralize(duration)}"
+    end   
 
     def format(string)
       Slack::Notifier::LinkFormatter.format(string)
diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb
index b0dcb52eba15db9b1f03f6f649d62a5125a9e3e7..a4a967c9bc94b02e6048a2dce870f8fb5f8a9a99 100644
--- a/app/models/project_services/teamcity_service.rb
+++ b/app/models/project_services/teamcity_service.rb
@@ -1,6 +1,4 @@
 class TeamcityService < CiService
-  include HTTParty
-
   prop_accessor :teamcity_url, :build_type, :username, :password
 
   validates :teamcity_url, presence: true, url: true, if: :activated?
@@ -64,15 +62,7 @@ class TeamcityService < CiService
   end
 
   def build_info(sha)
-    url = URI.join(
-      teamcity_url,
-      "/httpAuth/app/rest/builds/branch:unspecified:any,number:#{sha}"
-    ).to_s
-    auth = {
-      username: username,
-      password: password
-    }
-    @response = HTTParty.get(url, verify: false, basic_auth: auth)
+    @response = get_path("httpAuth/app/rest/builds/branch:unspecified:any,number:#{sha}")
   end
 
   def build_page(sha, ref)
@@ -81,14 +71,11 @@ class TeamcityService < CiService
     if @response.code != 200
       # If actual build link can't be determined,
       # send user to build summary page.
-      URI.join(teamcity_url, "/viewLog.html?buildTypeId=#{build_type}").to_s
+      build_url("viewLog.html?buildTypeId=#{build_type}")
     else
       # If actual build link is available, go to build result page.
       built_id = @response['build']['id']
-      URI.join(
-        teamcity_url,
-        "/viewLog.html?buildId=#{built_id}&buildTypeId=#{build_type}"
-      ).to_s
+      build_url("viewLog.html?buildId=#{built_id}&buildTypeId=#{build_type}")
     end
   end
 
@@ -123,8 +110,8 @@ class TeamcityService < CiService
 
     branch = Gitlab::Git.ref_name(data[:ref])
 
-    self.class.post(
-      URI.join(teamcity_url, '/httpAuth/app/rest/buildQueue').to_s,
+    HTTParty.post(
+      build_url('httpAuth/app/rest/buildQueue'),
       body: "<build branchName=\"#{branch}\">"\
             "<buildType id=\"#{build_type}\"/>"\
             '</build>',
@@ -132,4 +119,18 @@ class TeamcityService < CiService
       basic_auth: auth
     )
   end
+
+  private
+
+  def build_url(path)
+    URI.join("#{teamcity_url}/", path).to_s
+  end
+
+  def get_path(path)
+    HTTParty.get(build_url(path), verify: false,
+                                  basic_auth: {
+                                    username: username,
+                                    password: password
+                                  })
+  end
 end
diff --git a/app/models/project_snippet.rb b/app/models/project_snippet.rb
index 86c11b82c36a27384c25c80dff423cc71b241616..e81ef887a12caaa602ec4a1850f560a28b47b110 100644
--- a/app/models/project_snippet.rb
+++ b/app/models/project_snippet.rb
@@ -12,5 +12,6 @@ class ProjectSnippet < Snippet
   # Scopes
   scope :fresh, -> { order("created_at DESC") }
 
-  participant :author, :notes
+  participant :author
+  participant :notes_with_associations
 end
diff --git a/app/models/project_team.rb b/app/models/project_team.rb
index 88e2a4bd8211254c07d554d53d8cd93fa4263751..5621cb56433f9c42d330f0968c3e200b4864747e 100644
--- a/app/models/project_team.rb
+++ b/app/models/project_team.rb
@@ -21,23 +21,13 @@ class ProjectTeam
     end
   end
 
-  def find(user_id)
-    user = project.users.find_by(id: user_id)
-
-    if group
-      user ||= group.users.find_by(id: user_id)
-    end
-
-    user
-  end
-
   def find_member(user_id)
-    member = project.project_members.find_by(user_id: user_id)
+    member = project.members.non_request.find_by(user_id: user_id)
 
     # If user is not in project members
     # we should check for group membership
     if group && !member
-      member = group.group_members.find_by(user_id: user_id)
+      member = group.members.non_request.find_by(user_id: user_id)
     end
 
     member
@@ -63,13 +53,10 @@ class ProjectTeam
     ProjectMember.truncate_team(project)
   end
 
-  def users
-    members
-  end
-
   def members
     @members ||= fetch_members
   end
+  alias_method :users, :members
 
   def guests
     @guests ||= fetch_members(:guests)
@@ -133,8 +120,14 @@ class ProjectTeam
     max_member_access(user.id) == Gitlab::Access::MASTER
   end
 
-  def member?(user_id)
-    !!find_member(user_id)
+  def member?(user, min_member_access = nil)
+    member = !!find_member(user.id)
+
+    if min_member_access
+      member && max_member_access(user.id) >= min_member_access
+    else
+      member
+    end
   end
 
   def human_max_access(user_id)
@@ -146,7 +139,7 @@ class ProjectTeam
   def max_member_access(user_id)
     access = []
 
-    project.project_members.each do |member|
+    project.members.non_request.each do |member|
       if member.user_id == user_id
         access << member.access_field if member.access_field
         break
@@ -154,7 +147,7 @@ class ProjectTeam
     end
 
     if group
-      group.group_members.each do |member|
+      group.members.non_request.each do |member|
         if member.user_id == user_id
           access << member.access_field if member.access_field
           break
@@ -169,6 +162,7 @@ class ProjectTeam
     access.compact.max
   end
 
+  private
 
   def max_invited_level(user_id)
     project.project_group_links.map do |group_link|
@@ -185,17 +179,15 @@ class ProjectTeam
     end.compact.max
   end
 
-  private
-
   def fetch_members(level = nil)
-    project_members = project.project_members
-    group_members = group ? group.group_members : []
+    project_members = project.members.non_request
+    group_members = group ? group.members.non_request : []
     invited_members = []
 
     if project.invited_groups.any? && project.allowed_to_share_with_group?
       project.project_group_links.each do |group_link|
         invited_group = group_link.group
-        im = invited_group.group_members
+        im = invited_group.members.non_request
 
         if level
           int_level = GroupMember.access_level_roles[level.to_s.singularize.titleize]
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index 2fa9373a929f2eafbf442064899996fd41ba2bc3..9ad236b57af3dc6bcfb4c46d0ce8e3355fc89f2e 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -1,6 +1,7 @@
 class ProjectWiki
   include Gitlab::ShellAdapter
   include Elastic::WikiRepositoriesSearch
+  include Gitlab::CurrentSettings
 
   MARKUPS = {
     'Markdown' => :markdown,
@@ -29,6 +30,10 @@ class ProjectWiki
     @project.path_with_namespace + ".wiki"
   end
 
+  def web_url
+    Gitlab::Routing.url_helpers.namespace_project_wiki_url(@project.namespace, @project, :home)
+  end
+
   def url_to_repo
     gitlab_shell.url_to_repo(path_with_namespace)
   end
@@ -154,6 +159,16 @@ class ProjectWiki
     wiki
   end
 
+  def hook_attrs
+    {
+      web_url: web_url,
+      git_ssh_url: ssh_url_to_repo,
+      git_http_url: http_url_to_repo,
+      path_with_namespace: path_with_namespace,
+      default_branch: default_branch
+    }
+  end
+
   private
 
   def init_repo(path_with_namespace)
@@ -179,6 +194,6 @@ class ProjectWiki
   end
 
   def update_elastic_index
-    index_blobs if Gitlab.config.elasticsearch.enabled
+    index_blobs if current_application_settings.elasticsearch_indexing?
   end
 end
diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb
index 7a16953b20f68036b84a5f06a55a64366670ddb4..17ead04f367fb8c855e813dc605b7764d63ab586 100644
--- a/app/models/remote_mirror.rb
+++ b/app/models/remote_mirror.rb
@@ -2,16 +2,19 @@
 #
 # Table name: remote_mirrors
 #
-#  id                        :integer          not null, primary key
-#  project_id                :integer
-#  url                       :string
-#  last_update_at            :datetime
-#  last_error                :string
-#  created_at                :datetime         not null
-#  updated_at                :datetime         not null
-#  last_successful_update_at :datetime
-#  update_status             :string
-#  enabled                   :boolean          default(TRUE)
+#  id                         :integer          not null, primary key
+#  project_id                 :integer
+#  url                        :string
+#  enabled                    :boolean          default(TRUE)
+#  update_status              :string
+#  last_update_at             :datetime
+#  last_successful_update_at  :datetime
+#  last_error                 :string
+#  encrypted_credentials      :text
+#  encrypted_credentials_iv   :string
+#  encrypted_credentials_salt :string
+#  created_at                 :datetime         not null
+#  updated_at                 :datetime         not null
 #
 
 class RemoteMirror < ActiveRecord::Base
@@ -21,15 +24,16 @@ class RemoteMirror < ActiveRecord::Base
                  key: Gitlab::Application.secrets.db_key_base,
                  marshal: true,
                  encode: true,
-                 mode: :per_attribute_iv_and_salt
+                 mode: :per_attribute_iv_and_salt,
+                 algorithm: 'aes-256-cbc'
 
   belongs_to :project
 
   validates :url, presence: true, url: { protocols: %w(ssh git http https), allow_blank: true }
   validate  :url_availability, if: :url_changed?
 
-  after_save :refresh_remote, if: :url_changed?
-  after_update :reset_fields, if: :url_changed?
+  after_save :refresh_remote, if: :mirror_url_changed?
+  after_update :reset_fields, if: :mirror_url_changed?
   after_destroy :remove_remote
 
   scope :enabled, -> { where(enabled: true) }
@@ -116,7 +120,7 @@ class RemoteMirror < ActiveRecord::Base
 
     result = URI.parse(url)
     result.password = '*****' if result.password
-    result.user = '*****' if result.user && result.user != "git" #tokens or other data may be saved as user
+    result.user = '*****' if result.user && result.user != "git" # tokens or other data may be saved as user
     result.to_s
   end
 
@@ -155,4 +159,8 @@ class RemoteMirror < ActiveRecord::Base
   def remove_remote
     project.repository.remove_remote(ref_name)
   end
+
+  def mirror_url_changed?
+    url_changed? || encrypted_credentials_changed?
+  end
 end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 9d2f45df412ea512153ede5d6a30ad6f8239da07..502e4a238fc0f3131dd55097d9606384a7f8da2e 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -205,6 +205,10 @@ class Repository
     end
   end
 
+  def config
+    raw_repository.rugged.config
+  end
+
   def add_remote(name, url)
     raw_repository.remote_add(name, url)
   rescue Rugged::ConfigError
@@ -219,12 +223,10 @@ class Repository
   end
 
   def set_remote_as_mirror(name)
-    remote_config = raw_repository.rugged.config
-
     # This is used by Gitlab Geo to define repository as equivalent as "git clone --mirror"
-    remote_config["remote.#{name}.fetch"] = 'refs/*:refs/*'
-    remote_config["remote.#{name}.mirror"] = true
-    remote_config["remote.#{name}.prune"] = true
+    config["remote.#{name}.fetch"] = 'refs/*:refs/*'
+    config["remote.#{name}.mirror"] = true
+    config["remote.#{name}.prune"] = true
   end
 
   def fetch_remote(remote, forced: false, no_tags: false)
@@ -295,7 +297,7 @@ class Repository
   def cache_keys
     %i(size branch_names tag_names commit_count
        readme version contribution_guide changelog
-       license_blob license_key)
+       license_blob license_key gitignore)
   end
 
   def build_cache
@@ -306,6 +308,10 @@ class Repository
     end
   end
 
+  def expire_gitignore
+    cache.expire(:gitignore)
+  end
+
   def expire_tags_cache
     cache.expire(:tag_names)
     @tags = nil
@@ -492,7 +498,7 @@ class Repository
 
   def blob_at(sha, path)
     unless Gitlab::Git.blank_ref?(sha)
-      Gitlab::Git::Blob.find(self, sha, path)
+      Blob.decorate(Gitlab::Git::Blob.find(self, sha, path))
     end
   end
 
@@ -522,9 +528,7 @@ class Repository
 
   def changelog
     cache.fetch(:changelog) do
-      tree(:head).blobs.find do |file|
-        file.name =~ /\A(changelog|history|changes|news)/i
-      end
+      file_on_head(/\A(changelog|history|changes|news)/i)
     end
   end
 
@@ -532,9 +536,7 @@ class Repository
     return nil unless head_exists?
 
     cache.fetch(:license_blob) do
-      tree(:head).blobs.find do |file|
-        file.name =~ /\A(licen[sc]e|copying)(\..+|\z)/i
-      end
+      file_on_head(/\A(licen[sc]e|copying)(\..+|\z)/i)
     end
   end
 
@@ -546,6 +548,14 @@ class Repository
     end
   end
 
+  def gitignore
+    return nil if !exists? || empty?
+
+    cache.fetch(:gitignore) do
+      file_on_head(/\A\.gitignore\z/)
+    end
+  end
+
   def gitlab_ci_yml
     return nil unless head_exists?
 
@@ -980,7 +990,7 @@ class Repository
 
   def search_files(query, ref)
     offset = 2
-    args = %W(#{Gitlab.config.git.bin_path} grep -i -I -n --before-context #{offset} --after-context #{offset} -e #{Regexp.escape(query)} #{ref || root_ref})
+    args = %W(#{Gitlab.config.git.bin_path} grep -i -I -n --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref})
     Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/)
   end
 
@@ -1166,4 +1176,8 @@ class Repository
   def head_exists?
     exists? && !empty? && !rugged.head_unborn?
   end
+
+  def file_on_head(regex)
+    tree(:head).blobs.find { |file| file.name =~ regex }
+  end
 end
diff --git a/app/models/service.rb b/app/models/service.rb
index 167611adb970a9be42b5413a670a3f13d585adce..f121240864846771fa569042c2ba4cd94889f106 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -16,8 +16,9 @@ class Service < ActiveRecord::Base
   after_initialize :initialize_properties
 
   after_commit :reset_updated_properties
+  after_commit :cache_project_has_external_issue_tracker
 
-  belongs_to :project
+  belongs_to :project, inverse_of: :services
   has_one :service_hook
 
   validates :project_id, presence: true, unless: Proc.new { |service| service.template? }
@@ -34,6 +35,7 @@ class Service < ActiveRecord::Base
   scope :note_hooks, -> { where(note_events: true, active: true) }
   scope :build_hooks, -> { where(build_events: true, active: true) }
   scope :wiki_page_hooks, -> { where(wiki_page_events: true, active: true) }
+  scope :external_issue_trackers, -> { issue_trackers.active.without_defaults }
 
   default_value_for :category, 'common'
 
@@ -194,4 +196,12 @@ class Service < ActiveRecord::Base
     service.project_id = project_id
     service if service.save
   end
+
+  private
+
+  def cache_project_has_external_issue_tracker
+    if project && !project.destroyed?
+      project.cache_has_external_issue_tracker
+    end
+  end
 end
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index 253960984a5ed535fb92b23eef8851d5147e44b3..29df20281fc44bd00909f7e5c86018e8e3e1ca92 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -31,7 +31,8 @@ class Snippet < ActiveRecord::Base
   scope :public_and_internal, -> { where(visibility_level: [Snippet::PUBLIC, Snippet::INTERNAL]) }
   scope :fresh,   -> { order("created_at DESC") }
 
-  participant :author, :notes
+  participant :author
+  participant :notes_with_associations
 
   def self.reference_prefix
     '$'
@@ -101,6 +102,10 @@ class Snippet < ActiveRecord::Base
     content.lines.count > 1000
   end
 
+  def notes_with_associations
+    notes.includes(:author)
+  end
+
   class << self
     # Searches for snippets with a matching title or file name.
     #
diff --git a/app/models/todo.rb b/app/models/todo.rb
index 3a09137332911565659ba36aff1888d698556b79..2792fa9b9a8a1c00a55fb470a30678a8e780935d 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -2,6 +2,7 @@ class Todo < ActiveRecord::Base
   ASSIGNED     = 1
   MENTIONED    = 2
   BUILD_FAILED = 3
+  MARKED       = 4
 
   belongs_to :author, class_name: "User"
   belongs_to :note
diff --git a/app/models/u2f_registration.rb b/app/models/u2f_registration.rb
new file mode 100644
index 0000000000000000000000000000000000000000..00b19686d482e985c1362f1c4ab9cf258fad754f
--- /dev/null
+++ b/app/models/u2f_registration.rb
@@ -0,0 +1,40 @@
+# Registration information for U2F (universal 2nd factor) devices, like Yubikeys
+
+class U2fRegistration < ActiveRecord::Base
+  belongs_to :user
+
+  def self.register(user, app_id, json_response, challenges)
+    u2f = U2F::U2F.new(app_id)
+    registration = self.new
+
+    begin
+      response = U2F::RegisterResponse.load_from_json(json_response)
+      registration_data = u2f.register!(challenges, response)
+      registration.update(certificate: registration_data.certificate,
+                          key_handle: registration_data.key_handle,
+                          public_key: registration_data.public_key,
+                          counter: registration_data.counter,
+                          user: user)
+    rescue JSON::ParserError, NoMethodError, ArgumentError
+      registration.errors.add(:base, 'Your U2F device did not send a valid JSON response.')
+    rescue U2F::Error => e
+      registration.errors.add(:base, e.message)
+    end
+
+    registration
+  end
+
+  def self.authenticate(user, app_id, json_response, challenges)
+    response = U2F::SignResponse.load_from_json(json_response)
+    registration = user.u2f_registrations.find_by_key_handle(response.key_handle)
+    u2f = U2F::U2F.new(app_id)
+
+    if registration
+      u2f.authenticate!(challenges, response, Base64.decode64(registration.public_key), registration.counter)
+      registration.update(counter: response.counter)
+      true
+    end
+  rescue JSON::ParserError, NoMethodError, ArgumentError, U2F::Error
+    false
+  end
+end
diff --git a/app/models/user.rb b/app/models/user.rb
index a5a105e1e8dabffb60ce45c724a4287a2a30a6ce..f54066222161ced5e0e6c581dedb03b2c3516b02 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -10,6 +10,8 @@ class User < ActiveRecord::Base
   include CaseSensitivity
   include TokenAuthenticatable
 
+  DEFAULT_NOTIFICATION_LEVEL = :participating
+
   add_authentication_token_field :authentication_token
 
   default_value_for :admin, false
@@ -20,14 +22,18 @@ class User < ActiveRecord::Base
   default_value_for :hide_no_password, false
   default_value_for :theme_id, gitlab_config.default_theme
 
+  attr_encrypted :otp_secret,
+    key:       Gitlab::Application.config.secret_key_base,
+    mode:      :per_attribute_iv_and_salt,
+    algorithm: 'aes-256-cbc'
+
   devise :two_factor_authenticatable,
          otp_secret_encryption_key: Gitlab::Application.config.secret_key_base
-  alias_attribute :two_factor_enabled, :otp_required_for_login
 
   devise :two_factor_backupable, otp_number_of_backup_codes: 10
   serialize :otp_backup_codes, JSON
 
-  devise :lockable, :async, :recoverable, :rememberable, :trackable,
+  devise :lockable, :recoverable, :rememberable, :trackable,
     :validatable, :omniauthable, :confirmable, :registerable
 
   attr_accessor :force_random_password
@@ -46,11 +52,11 @@ class User < ActiveRecord::Base
   has_many :keys, dependent: :destroy
   has_many :emails, dependent: :destroy
   has_many :identities, dependent: :destroy, autosave: true
+  has_many :u2f_registrations, dependent: :destroy
 
   # Groups
   has_many :members, dependent: :destroy
-  has_many :project_members, source: 'ProjectMember'
-  has_many :group_members, source: 'GroupMember'
+  has_many :group_members, dependent: :destroy, source: 'GroupMember'
   has_many :groups, through: :group_members
   has_many :owned_groups, -> { where members: { access_level: Gitlab::Access::OWNER } }, through: :group_members, source: :group
   has_many :masters_groups, -> { where members: { access_level: Gitlab::Access::MASTER } }, through: :group_members, source: :group
@@ -58,13 +64,13 @@ class User < ActiveRecord::Base
   # Projects
   has_many :groups_projects,          through: :groups, source: :projects
   has_many :personal_projects,        through: :namespace, source: :projects
+  has_many :project_members,          dependent: :destroy, class_name: 'ProjectMember'
   has_many :projects,                 through: :project_members
   has_many :created_projects,         foreign_key: :creator_id, class_name: 'Project'
   has_many :users_star_projects,      dependent: :destroy
   has_many :starred_projects,         through: :users_star_projects, source: :project
 
   has_many :snippets,                 dependent: :destroy, foreign_key: :author_id, class_name: "Snippet"
-  has_many :project_members,          dependent: :destroy, class_name: 'ProjectMember'
   has_many :issues,                   dependent: :destroy, foreign_key: :author_id
   has_many :notes,                    dependent: :destroy, foreign_key: :author_id
   has_many :merge_requests,           dependent: :destroy, foreign_key: :author_id
@@ -81,6 +87,8 @@ class User < ActiveRecord::Base
   has_many :builds,                   dependent: :nullify, class_name: 'Ci::Build'
   has_many :todos,                    dependent: :destroy
   has_many :notification_settings,    dependent: :destroy
+  has_many :award_emoji,              as: :awardable, dependent: :destroy
+  has_many :path_locks,               dependent: :destroy
 
   #
   # Validations
@@ -95,7 +103,6 @@ class User < ActiveRecord::Base
     presence: true,
     uniqueness: { case_sensitive: false }
 
-  validates :notification_level, presence: true
   validate :namespace_uniq, if: ->(user) { user.username_changed? }
   validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
   validate :unique_email, if: ->(user) { user.email_changed? }
@@ -129,13 +136,6 @@ class User < ActiveRecord::Base
   # Note: When adding an option, it MUST go on the end of the array.
   enum project_view: [:readme, :activity, :files]
 
-  # Notification level
-  # Note: When adding an option, it MUST go on the end of the array.
-  #
-  # TODO: Add '_prefix: :notification' to enum when update to Rails 5. https://github.com/rails/rails/pull/19813
-  # Because user.notification_disabled? is much better than user.disabled?
-  enum notification_level: [:disabled, :participating, :watch, :global, :mention]
-
   alias_attribute :private_token, :authentication_token
 
   delegate :path, to: :namespace, allow_nil: true, prefix: true
@@ -173,12 +173,20 @@ class User < ActiveRecord::Base
   scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') }
   scope :subscribed_for_admin_email, -> { where(admin_email_unsubscribed_at: nil) }
   scope :ldap, -> { joins(:identities).where('identities.provider LIKE ?', 'ldap%') }
-  scope :with_two_factor,    -> { where(two_factor_enabled: true) }
-  scope :without_two_factor, -> { where(two_factor_enabled: false) }
   scope :with_provider, ->(provider) do
     joins(:identities).where(identities: { provider: provider })
   end
 
+  def self.with_two_factor
+    joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id").
+      where("u2f.id IS NOT NULL OR otp_required_for_login = ?", true).distinct(arel_table[:id])
+  end
+
+  def self.without_two_factor
+    joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id").
+      where("u2f.id IS NULL AND otp_required_for_login = ?", false)
+  end
+
   #
   # Class methods
   #
@@ -349,14 +357,29 @@ class User < ActiveRecord::Base
   end
 
   def disable_two_factor!
-    update_attributes(
-      two_factor_enabled:          false,
-      encrypted_otp_secret:        nil,
-      encrypted_otp_secret_iv:     nil,
-      encrypted_otp_secret_salt:   nil,
-      otp_grace_period_started_at: nil,
-      otp_backup_codes:            nil
-    )
+    transaction do
+      update_attributes(
+        otp_required_for_login:      false,
+        encrypted_otp_secret:        nil,
+        encrypted_otp_secret_iv:     nil,
+        encrypted_otp_secret_salt:   nil,
+        otp_grace_period_started_at: nil,
+        otp_backup_codes:            nil
+      )
+      self.u2f_registrations.destroy_all
+    end
+  end
+
+  def two_factor_enabled?
+    two_factor_otp_enabled? || two_factor_u2f_enabled?
+  end
+
+  def two_factor_otp_enabled?
+    self.otp_required_for_login?
+  end
+
+  def two_factor_u2f_enabled?
+    self.u2f_registrations.exists?
   end
 
   def namespace_uniq
@@ -414,8 +437,8 @@ class User < ActiveRecord::Base
   end
 
   # Returns projects user is authorized to access.
-  def authorized_projects
-    Project.where("projects.id IN (#{projects_union.to_sql})")
+  def authorized_projects(min_access_level = nil)
+    Project.where("projects.id IN (#{projects_union(min_access_level).to_sql})")
   end
 
   def viewable_starred_projects
@@ -807,13 +830,49 @@ class User < ActiveRecord::Base
     notification_settings.find_or_initialize_by(source: source)
   end
 
+  # Lazy load global notification setting
+  # Initializes User setting with Participating level if setting not persisted
+  def global_notification_setting
+    return @global_notification_setting if defined?(@global_notification_setting)
+
+    @global_notification_setting = notification_settings.find_or_initialize_by(source: nil)
+    @global_notification_setting.update_attributes(level: NotificationSetting.levels[DEFAULT_NOTIFICATION_LEVEL]) unless @global_notification_setting.persisted?
+
+    @global_notification_setting
+  end
+
+  def assigned_open_merge_request_count(force: false)
+    Rails.cache.fetch(['users', id, 'assigned_open_merge_request_count'], force: force) do
+      assigned_merge_requests.opened.count
+    end
+  end
+
+  def assigned_open_issues_count(force: false)
+    Rails.cache.fetch(['users', id, 'assigned_open_issues_count'], force: force) do
+      assigned_issues.opened.count
+    end
+  end
+
+  def update_cache_counts
+    assigned_open_merge_request_count(force: true)
+    assigned_open_issues_count(force: true)
+  end
+
   private
 
-  def projects_union
-    Gitlab::SQL::Union.new([personal_projects.select(:id),
-                            groups_projects.select(:id),
-                            projects.select(:id),
-                            groups.joins(:shared_projects).select(:project_id)])
+  def projects_union(min_access_level = nil)
+    relations = [personal_projects.select(:id),
+                 groups_projects.select(:id),
+                 projects.select(:id),
+                 groups.joins(:shared_projects).select(:project_id)]
+
+
+    if min_access_level
+      scope = { access_level: Gitlab::Access.values.select { |access| access >= min_access_level } }
+      relations = [relations.shift] + relations.map { |relation| relation.where(members: scope) }
+    end
+
+    Gitlab::SQL::Union.new(relations)
   end
 
   def ci_projects_union
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index 2bbab643e6976134b869eab34d934d1e6b1d03c7..e57b95f21ecca2ba4ed843c3471a4ed79f85995e 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -1,13 +1,13 @@
 module Auth
   class ContainerRegistryAuthenticationService < BaseService
+    include Gitlab::CurrentSettings
+
     AUDIENCE = 'container_registry'
 
     def execute
       return error('not found', 404) unless registry.enabled
 
-      if params[:offline_token]
-        return error('unauthorized', 401) unless current_user || project
-      else
+      unless current_user || project
         return error('forbidden', 403) unless scope
       end
 
@@ -19,6 +19,7 @@ module Auth
       token = JSONWebToken::RSAToken.new(registry.key)
       token.issuer = registry.issuer
       token.audience = AUDIENCE
+      token.expire_time = token_expire_at
       token[:access] = names.map do |name|
         { type: 'repository', name: name, actions: %w(*) }
       end
@@ -32,6 +33,7 @@ module Auth
       token.issuer = registry.issuer
       token.audience = params[:service]
       token.subject = current_user.try(:username)
+      token.expire_time = ContainerRegistryAuthenticationService.token_expire_at
       token[:access] = accesses.compact
       token
     end
@@ -77,5 +79,9 @@ module Auth
     def registry
       Gitlab.config.registry
     end
+
+    def self.token_expire_at
+      Time.now + current_application_settings.container_registry_token_expire_delay.minutes
+    end
   end
 end
diff --git a/app/services/ci/create_builds_service.rb b/app/services/ci/create_builds_service.rb
index 18274ce24e229706d30a74717461ec9c0dc42797..3a74ae094e81e5061df6040e9263f9bbdbe7d963 100644
--- a/app/services/ci/create_builds_service.rb
+++ b/app/services/ci/create_builds_service.rb
@@ -1,11 +1,11 @@
 module Ci
   class CreateBuildsService
-    def initialize(commit)
-      @commit = commit
+    def initialize(pipeline)
+      @pipeline = pipeline
     end
 
     def execute(stage, user, status, trigger_request = nil)
-      builds_attrs = config_processor.builds_for_stage_and_ref(stage, @commit.ref, @commit.tag, trigger_request)
+      builds_attrs = config_processor.builds_for_stage_and_ref(stage, @pipeline.ref, @pipeline.tag, trigger_request)
 
       # check when to create next build
       builds_attrs = builds_attrs.select do |build_attrs|
@@ -21,23 +21,24 @@ module Ci
 
       builds_attrs.map do |build_attrs|
         # don't create the same build twice
-        unless @commit.builds.find_by(ref: @commit.ref, tag: @commit.tag,
-                                      trigger_request: trigger_request, name: build_attrs[:name])
+        unless @pipeline.builds.find_by(ref: @pipeline.ref, tag: @pipeline.tag,
+                                        trigger_request: trigger_request, name: build_attrs[:name])
           build_attrs.slice!(:name,
                              :commands,
                              :tag_list,
                              :options,
                              :allow_failure,
                              :stage,
-                             :stage_idx)
+                             :stage_idx,
+                             :environment)
 
-          build_attrs.merge!(ref: @commit.ref,
-                             tag: @commit.tag,
+          build_attrs.merge!(ref: @pipeline.ref,
+                             tag: @pipeline.tag,
                              trigger_request: trigger_request,
                              user: user,
-                             project: @commit.project)
+                             project: @pipeline.project)
 
-          @commit.builds.create!(build_attrs)
+          @pipeline.builds.create!(build_attrs)
         end
       end
     end
@@ -45,7 +46,7 @@ module Ci
     private
 
     def config_processor
-      @config_processor ||= @commit.config_processor
+      @config_processor ||= @pipeline.config_processor
     end
   end
 end
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index 5bc0c31cb42edfd4b2241a4485a05169ac29d091..a7751b8effcb9b9e7271b7fe8ae54fb944ae49ac 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -1,7 +1,7 @@
 module Ci
   class CreatePipelineService < BaseService
     def execute
-      pipeline = project.ci_commits.new(params)
+      pipeline = project.pipelines.new(params)
 
       unless ref_names.include?(params[:ref])
         pipeline.errors.add(:base, 'Reference not found')
@@ -19,7 +19,7 @@ module Ci
       end
 
       begin
-        Ci::Commit.transaction do
+        Ci::Pipeline.transaction do
           pipeline.sha = commit.id
 
           unless pipeline.config_processor
diff --git a/app/services/ci/create_trigger_request_service.rb b/app/services/ci/create_trigger_request_service.rb
index 993acf11db968a158f0e116960167f19ba3673e4..1e629cf119aa5120997d1dbfa892b741c38deb1d 100644
--- a/app/services/ci/create_trigger_request_service.rb
+++ b/app/services/ci/create_trigger_request_service.rb
@@ -7,14 +7,14 @@ module Ci
       # check if ref is tag
       tag = project.repository.find_tag(ref).present?
 
-      ci_commit = project.ci_commits.create(sha: commit.sha, ref: ref, tag: tag)
+      pipeline = project.pipelines.create(sha: commit.sha, ref: ref, tag: tag)
 
       trigger_request = trigger.trigger_requests.create!(
         variables: variables,
-        commit: ci_commit,
+        pipeline: pipeline,
       )
 
-      if ci_commit.create_builds(nil, trigger_request)
+      if pipeline.create_builds(nil, trigger_request)
         trigger_request
       end
     end
diff --git a/app/services/ci/image_for_build_service.rb b/app/services/ci/image_for_build_service.rb
index 3018f27ec059e47921efcfe0105f1da9ecc660a3..75d847d5bee9043d35ce0ed33a39163c064f12d0 100644
--- a/app/services/ci/image_for_build_service.rb
+++ b/app/services/ci/image_for_build_service.rb
@@ -3,9 +3,9 @@ module Ci
     def execute(project, opts)
       sha = opts[:sha] || ref_sha(project, opts[:ref])
 
-      ci_commits = project.ci_commits.where(sha: sha)
-      ci_commits = ci_commits.where(ref: opts[:ref]) if opts[:ref]
-      image_name = image_for_status(ci_commits.status)
+      pipelines = project.pipelines.where(sha: sha)
+      pipelines = pipelines.where(ref: opts[:ref]) if opts[:ref]
+      image_name = image_for_status(pipelines.status)
 
       image_path = Rails.root.join('public/ci', image_name)
       OpenStruct.new(path: image_path, name: image_name)
diff --git a/app/services/ci/register_build_service.rb b/app/services/ci/register_build_service.rb
index 4ff268a6f067b15191e644d571a070ff44340acd..f0ed09a629ad2ebe2cbd08f8b178e96c07e7d363 100644
--- a/app/services/ci/register_build_service.rb
+++ b/app/services/ci/register_build_service.rb
@@ -7,15 +7,19 @@ module Ci
 
       builds =
         if current_runner.shared?
-          # don't run projects which have not enables shared runners
-          builds.joins(:project).where(projects: { builds_enabled: true, shared_runners_enabled: true })
+          builds.
+            # don't run projects which have not enabled shared runners
+            joins(:project).where(projects: { builds_enabled: true, shared_runners_enabled: true }).
+
+            # this returns builds that are ordered by number of running builds
+            # we prefer projects that don't use shared runners at all
+            joins("LEFT JOIN (#{running_builds_for_shared_runners.to_sql}) AS project_builds ON ci_builds.gl_project_id=project_builds.gl_project_id").
+            order('COALESCE(project_builds.running_builds, 0) ASC', 'ci_builds.id ASC')
         else
-          # do run projects which are only assigned to this runner
-          builds.where(project: current_runner.projects.where(builds_enabled: true))
+          # do run projects which are only assigned to this runner (FIFO)
+          builds.where(project: current_runner.projects.where(builds_enabled: true)).order('created_at ASC')
         end
 
-      builds = builds.order('created_at ASC')
-
       build = builds.find do |build|
         build.can_be_served?(current_runner)
       end
@@ -35,5 +39,12 @@ module Ci
     rescue StateMachines::InvalidTransition
       nil
     end
+
+    private
+
+    def running_builds_for_shared_runners
+      Ci::Build.running.where(runner: Ci::Runner.shared).
+        group(:gl_project_id).select(:gl_project_id, 'count(*) AS running_builds')
+    end
   end
 end
diff --git a/app/services/create_commit_builds_service.rb b/app/services/create_commit_builds_service.rb
index 377f4bab328f0e46d291731905a8c2944d7a2bf0..2c35738be5aa966a962d3802ce49299e7605ff83 100644
--- a/app/services/create_commit_builds_service.rb
+++ b/app/services/create_commit_builds_service.rb
@@ -21,23 +21,23 @@ class CreateCommitBuildsService
       return false
     end
 
-    commit = Ci::Commit.new(project: project, sha: sha, ref: ref, before_sha: before_sha, tag: tag)
+    pipeline = Ci::Pipeline.new(project: project, sha: sha, ref: ref, before_sha: before_sha, tag: tag)
 
-    # Skip creating ci_commit when no gitlab-ci.yml is found
-    unless commit.ci_yaml_file
+    # Skip creating pipeline when no gitlab-ci.yml is found
+    unless pipeline.ci_yaml_file
       return false
     end
 
-    # Create a new ci_commit
-    commit.save!
+    # Create a new pipeline
+    pipeline.save!
 
     # Skip creating builds for commits that have [ci skip]
-    unless commit.skip_ci?
+    unless pipeline.skip_ci?
       # Create builds for commit
-      commit.create_builds(user)
+      pipeline.create_builds(user)
     end
 
-    commit.touch
-    commit
+    pipeline.touch
+    pipeline
   end
 end
diff --git a/app/services/create_deployment_service.rb b/app/services/create_deployment_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..efeb9df9527f6263aea1c40e76f8bb24dc20f89a
--- /dev/null
+++ b/app/services/create_deployment_service.rb
@@ -0,0 +1,18 @@
+require_relative 'base_service'
+
+class CreateDeploymentService < BaseService
+  def execute(deployable = nil)
+    environment = project.environments.find_or_create_by(
+      name: params[:environment]
+    )
+
+    project.deployments.create(
+      environment: environment,
+      ref: params[:ref],
+      tag: params[:tag],
+      sha: params[:sha],
+      user: current_user,
+      deployable: deployable
+    )
+  end
+end
diff --git a/app/services/git_hooks_service.rb b/app/services/git_hooks_service.rb
index 8f5c3393dfc8e2b56fcb78817e8c15a98a4e2e63..d7a0c25a044d4e55304766463c789a3cda1fdb52 100644
--- a/app/services/git_hooks_service.rb
+++ b/app/services/git_hooks_service.rb
@@ -3,7 +3,7 @@ class GitHooksService
 
   def execute(user, repo_path, oldrev, newrev, ref)
     @repo_path  = repo_path
-    @user       = Gitlab::ShellEnv.gl_id(user)
+    @user       = Gitlab::GlId.gl_id(user)
     @oldrev     = oldrev
     @newrev     = newrev
     @ref        = ref
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index 92f02a6b4f14bfd6bac8b2964594570737e46084..03955bb7dd18a7cf087be6ac2395bff9b4a8f1ae 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -53,10 +53,6 @@ class GitPushService < BaseService
     # could cause the last commit of a merge request to change.
     update_merge_requests
 
-    # Checks if the main language has changed in the project and if so
-    # it updates it accordingly
-    update_main_language
-
     perform_housekeeping
   end
 
@@ -64,19 +60,6 @@ class GitPushService < BaseService
     @project.repository.copy_gitattributes(params[:ref])
   end
 
-  def update_main_language
-    # Performance can be bad so for now only check main_language once
-    # See https://gitlab.com/gitlab-org/gitlab-ce/issues/14937
-    return if @project.main_language.present?
-
-    return unless is_default_branch?
-    return unless push_to_new_branch? || push_to_existing_branch?
-
-    current_language = @project.repository.main_language
-    @project.update_attributes(main_language: current_language)
-    true
-  end
-
   protected
 
   def update_merge_requests
@@ -91,7 +74,7 @@ class GitPushService < BaseService
     CreateCommitBuildsService.new.execute(@project, current_user, build_push_data, mirror_update: mirror_update)
     ProjectCacheWorker.perform_async(@project.id)
 
-    index_commits_blobs if Gitlab.config.elasticsearch.enabled
+    index_commits_blobs if current_application_settings.elasticsearch_indexing?
   end
 
   def index_commits_blobs
diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb
index c5a75a3367798e383ef9696b3e111e51410af22b..675ceee24be73ed53c600a4832cf3a5854496839 100644
--- a/app/services/git_tag_push_service.rb
+++ b/app/services/git_tag_push_service.rb
@@ -28,7 +28,7 @@ class GitTagPushService < BaseService
     commits = []
     message = nil
 
-    if !Gitlab::Git.blank_ref?(params[:newrev])
+    unless Gitlab::Git.blank_ref?(params[:newrev])
       tag_name = Gitlab::Git.ref_name(params[:ref])
       tag = project.repository.find_tag(tag_name)
       if tag && tag.target == params[:newrev]
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 2b16089df1bef1957bbe1ab37ec27a390489b765..e3dc569152cda3af69f5719f6a94fffa0461095a 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -45,6 +45,8 @@ class IssuableBaseService < BaseService
 
     unless can?(current_user, ability, project)
       params.delete(:milestone_id)
+      params.delete(:add_label_ids)
+      params.delete(:remove_label_ids)
       params.delete(:label_ids)
       params.delete(:assignee_id)
     end
@@ -67,10 +69,34 @@ class IssuableBaseService < BaseService
   end
 
   def filter_labels
-    return if params[:label_ids].to_a.empty?
+    if params[:add_label_ids].present? || params[:remove_label_ids].present?
+      params.delete(:label_ids)
+
+      filter_labels_in_param(:add_label_ids)
+      filter_labels_in_param(:remove_label_ids)
+    else
+      filter_labels_in_param(:label_ids)
+    end
+  end
+
+  def filter_labels_in_param(key)
+    return if params[key].to_a.empty?
 
-    params[:label_ids] =
-      project.labels.where(id: params[:label_ids]).pluck(:id)
+    params[key] = project.labels.where(id: params[key]).pluck(:id)
+  end
+
+  def update_issuable(issuable, attributes)
+    issuable.with_transaction_returning_status do
+      add_label_ids = attributes.delete(:add_label_ids)
+      remove_label_ids = attributes.delete(:remove_label_ids)
+
+      issuable.label_ids |= add_label_ids if add_label_ids
+      issuable.label_ids -= remove_label_ids if remove_label_ids
+
+      issuable.assign_attributes(attributes.merge(updated_by: current_user))
+
+      issuable.save
+    end
   end
 
   def update(issuable)
@@ -78,7 +104,7 @@ class IssuableBaseService < BaseService
     filter_params
     old_labels = issuable.labels.to_a
 
-    if params.present? && issuable.update_attributes(params.merge(updated_by: current_user))
+    if params.present? && update_issuable(issuable, params)
       issuable.reset_events_cache
       handle_common_system_notes(issuable, old_labels: old_labels)
       handle_changes(issuable, old_labels: old_labels)
diff --git a/app/services/issues/bulk_update_service.rb b/app/services/issues/bulk_update_service.rb
index de8387c49008c76b78344668495964385fdc4302..15825b81685b4eb925fdf0a8cb7292ace591850d 100644
--- a/app/services/issues/bulk_update_service.rb
+++ b/app/services/issues/bulk_update_service.rb
@@ -4,9 +4,9 @@ module Issues
       issues_ids   = params.delete(:issues_ids).split(",")
       issue_params = params
 
-      issue_params.delete(:state_event)   unless issue_params[:state_event].present?
-      issue_params.delete(:milestone_id)  unless issue_params[:milestone_id].present?
-      issue_params.delete(:assignee_id)   unless issue_params[:assignee_id].present?
+      %i(state_event milestone_id assignee_id add_label_ids remove_label_ids).each do |key|
+        issue_params.delete(key) unless issue_params[key].present?
+      end
 
       issues = Issue.where(id: issues_ids)
       issues.each do |issue|
diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb
index e61628086f0e8c4ead6a4dd2e17d5dfe5af11141..ab667456db772ba02de888860d307acaa918b264 100644
--- a/app/services/issues/move_service.rb
+++ b/app/services/issues/move_service.rb
@@ -24,6 +24,7 @@ module Issues
         @new_issue = create_new_issue
 
         rewrite_notes
+        rewrite_award_emoji
         add_note_moved_from
 
         # Old issue tasks
@@ -72,6 +73,14 @@ module Issues
       end
     end
 
+    def rewrite_award_emoji
+      @old_issue.award_emoji.each do |award|
+        new_award = award.dup
+        new_award.awardable = @new_issue
+        new_award.save
+      end
+    end
+
     def rewrite_content(content)
       return unless content
 
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index 9d7fca6882db3c0d53544d8d7ba284340a94ea3f..bc93ba2552d185ab235c0f1c16b46d50ade2cd3a 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -55,12 +55,12 @@ module MergeRequests
 
     def each_merge_request(commit_status)
       merge_request_from(commit_status).each do |merge_request|
-        ci_commit = merge_request.ci_commit
+        pipeline = merge_request.pipeline
 
-        next unless ci_commit
-        next unless ci_commit.sha == commit_status.sha
+        next unless pipeline
+        next unless pipeline.sha == commit_status.sha
 
-        yield merge_request, ci_commit
+        yield merge_request, pipeline
       end
     end
   end
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index 19074ae808c18f7d25b0cf57f2ece1debee8f266..d607d441ad5f4a89568a618c775139122810f7fe 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -9,7 +9,7 @@ module MergeRequests
     attr_reader :merge_request
 
     def execute(merge_request)
-      if @project.merge_requests_ff_only_enabled && !self.is_a?(FfMergeService)
+      if project.merge_requests_ff_only_enabled && !self.is_a?(FfMergeService)
         FfMergeService.new(project, current_user, params).execute(merge_request)
         return
       end
@@ -29,6 +29,8 @@ module MergeRequests
     end
 
     def hooks_validation_pass?(merge_request)
+      return true if project.merge_requests_ff_only_enabled
+
       git_hook = merge_request.project.git_hook
       return true unless git_hook
 
diff --git a/app/services/merge_requests/merge_when_build_succeeds_service.rb b/app/services/merge_requests/merge_when_build_succeeds_service.rb
index 8fd6a4ea1f68ed5e240333fc500b3693b2b54e15..12edfb2d671c258d875721c7087e2ed0669e5ad8 100644
--- a/app/services/merge_requests/merge_when_build_succeeds_service.rb
+++ b/app/services/merge_requests/merge_when_build_succeeds_service.rb
@@ -20,10 +20,10 @@ module MergeRequests
 
     # Triggers the automatic merge of merge_request once the build succeeds
     def trigger(commit_status)
-      each_merge_request(commit_status) do |merge_request, ci_commit|
+      each_merge_request(commit_status) do |merge_request, pipeline|
         next unless merge_request.merge_when_build_succeeds?
         next unless merge_request.mergeable?
-        next unless ci_commit.success?
+        next unless pipeline.success?
 
         MergeWorker.perform_async(merge_request.id, merge_request.merge_user_id, merge_request.merge_params)
       end
diff --git a/app/services/merge_requests/rebase_service.rb b/app/services/merge_requests/rebase_service.rb
index d0f09a99428707ff46c30b4fac9d5f7117b35724..877b41245fe52fed8656483515f10093726c9b77 100644
--- a/app/services/merge_requests/rebase_service.rb
+++ b/app/services/merge_requests/rebase_service.rb
@@ -21,15 +21,17 @@ module MergeRequests
     end
 
     def rebase
-      Gitlab::ShellEnv.set_env(current_user)
-
       if merge_request.rebase_in_progress?
         log('Rebase task canceled: Another rebase is already in progress')
         return false
       end
 
       # Clone
-      output, status = popen(%W(git clone -b #{merge_request.source_branch} -- #{source_project.repository.path_to_repo} #{tree_path}))
+      output, status = popen(
+        %W(git clone -b #{merge_request.source_branch} -- #{source_project.repository.path_to_repo} #{tree_path}),
+        nil,
+        git_env
+      )
 
       unless status.zero?
         log('Failed to clone repository for rebase:')
@@ -38,7 +40,11 @@ module MergeRequests
       end
 
       # Rebase
-      output, status = popen(%W(git pull --rebase #{target_project.repository.path_to_repo} #{merge_request.target_branch}), tree_path)
+      output, status = popen(
+        %W(git pull --rebase #{target_project.repository.path_to_repo} #{merge_request.target_branch}),
+        tree_path,
+        git_env
+      )
 
       unless status.zero?
         log('Failed to rebase branch:')
@@ -47,7 +53,11 @@ module MergeRequests
       end
 
       # Push
-      output, status = popen(%W(git push -f origin #{merge_request.source_branch}), tree_path)
+      output, status = popen(
+        %W(git push -f origin #{merge_request.source_branch}),
+        tree_path,
+        git_env
+      )
 
       unless status.zero?
         log('Failed to push rebased branch:')
@@ -61,7 +71,6 @@ module MergeRequests
       log(ex.message)
     ensure
       clean_dir
-      Gitlab::ShellEnv.reset_env
     end
 
     def source_project
@@ -83,5 +92,9 @@ module MergeRequests
     def clean_dir
       FileUtils.rm_rf(tree_path) if File.exist?(tree_path)
     end
+
+    def git_env
+      { 'GL_ID' => Gitlab::GlId.gl_id(current_user) }
+    end
   end
 end
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index 01586994813cca5dcca3fe8f628c058d256425dd..02fca5c0ea30c4486d820a459b2ce196aa383b4e 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -5,7 +5,12 @@ module Notes
       note.author = current_user
       note.system = false
 
-      return unless valid_project?(note)
+      if note.award_emoji?
+        noteable = note.noteable
+        todo_service.new_award_emoji(noteable, current_user)
+
+        return noteable.create_award_emoji(note.award_emoji_name, current_user)
+      end
 
       if note.save
         # Finish the harder work in the background
@@ -15,14 +20,5 @@ module Notes
 
       note
     end
-
-    private
-
-    def valid_project?(note)
-      return false unless project
-      return true if note.for_commit?
-
-      note.noteable.try(:project) == project
-    end
   end
 end
diff --git a/app/services/notes/post_process_service.rb b/app/services/notes/post_process_service.rb
index e818f58d13ca402309f0620c4ed7f58c05ee52b8..534c48aefffed7499805cfa50ac082c2cc01a6cb 100644
--- a/app/services/notes/post_process_service.rb
+++ b/app/services/notes/post_process_service.rb
@@ -8,7 +8,7 @@ module Notes
 
     def execute
       # Skip system notes, like status changes and cross-references and awards
-      unless @note.system || @note.is_award
+      unless @note.system?
         EventCreateService.new.leave_note(@note, @note.author)
         @note.create_cross_references!
         execute_note_hooks
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 42ec1ac9e1a15ed2b6a76c0cf001fe302e5ec492..f804ac171c4915f33acd75225d31d879594fc619 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -130,8 +130,7 @@ class NotificationService
 
     # ignore gitlab service messages
     return true if note.note.start_with?('Status changed to closed')
-    return true if note.cross_reference? && note.system == true
-    return true if note.is_award
+    return true if note.cross_reference? && note.system?
 
     target = note.noteable
 
@@ -174,16 +173,26 @@ class NotificationService
     end
   end
 
+  # Project access request
+  def new_project_access_request(project_member)
+    mailer.member_access_requested_email(project_member.real_source_type, project_member.id).deliver_later
+  end
+
+  def decline_project_access_request(project_member)
+    mailer.member_access_denied_email(project_member.real_source_type, project_member.project.id, project_member.user.id).deliver_later
+  end
+
   def invite_project_member(project_member, token)
-    mailer.project_member_invited_email(project_member.id, token).deliver_later
+    mailer.member_invited_email(project_member.real_source_type, project_member.id, token).deliver_later
   end
 
   def accept_project_invite(project_member)
-    mailer.project_invite_accepted_email(project_member.id).deliver_later
+    mailer.member_invite_accepted_email(project_member.real_source_type, project_member.id).deliver_later
   end
 
   def decline_project_invite(project_member)
-    mailer.project_invite_declined_email(
+    mailer.member_invite_declined_email(
+      project_member.real_source_type,
       project_member.project.id,
       project_member.invite_email,
       project_member.access_level,
@@ -192,23 +201,33 @@ class NotificationService
   end
 
   def new_project_member(project_member)
-    mailer.project_access_granted_email(project_member.id).deliver_later
+    mailer.member_access_granted_email(project_member.real_source_type, project_member.id).deliver_later
   end
 
   def update_project_member(project_member)
-    mailer.project_access_granted_email(project_member.id).deliver_later
+    mailer.member_access_granted_email(project_member.real_source_type, project_member.id).deliver_later
+  end
+
+  # Group access request
+  def new_group_access_request(group_member)
+    mailer.member_access_requested_email(group_member.real_source_type, group_member.id).deliver_later
+  end
+
+  def decline_group_access_request(group_member)
+    mailer.member_access_denied_email(group_member.real_source_type, group_member.group.id, group_member.user.id).deliver_later
   end
 
   def invite_group_member(group_member, token)
-    mailer.group_member_invited_email(group_member.id, token).deliver_later
+    mailer.member_invited_email(group_member.real_source_type, group_member.id, token).deliver_later
   end
 
   def accept_group_invite(group_member)
-    mailer.group_invite_accepted_email(group_member.id).deliver_later
+    mailer.member_invite_accepted_email(group_member.id).deliver_later
   end
 
   def decline_group_invite(group_member)
-    mailer.group_invite_declined_email(
+    mailer.member_invite_declined_email(
+      group_member.real_source_type,
       group_member.group.id,
       group_member.invite_email,
       group_member.access_level,
@@ -217,11 +236,11 @@ class NotificationService
   end
 
   def new_group_member(group_member)
-    mailer.group_access_granted_email(group_member.id).deliver_later
+    mailer.member_access_granted_email(group_member.real_source_type, group_member.id).deliver_later
   end
 
   def update_group_member(group_member)
-    mailer.group_access_granted_email(group_member.id).deliver_later
+    mailer.member_access_granted_email(group_member.real_source_type, group_member.id).deliver_later
   end
 
   def project_was_moved(project, old_path_with_namespace)
@@ -280,10 +299,11 @@ class NotificationService
   end
 
   def users_with_global_level_watch(ids)
-    User.where(
-      id: ids,
-      notification_level: NotificationSetting.levels[:watch]
-    ).pluck(:id)
+    NotificationSetting.where(
+      user_id: ids,
+      source_type: nil,
+      level: NotificationSetting.levels[:watch]
+    ).pluck(:user_id)
   end
 
   # Build a list of users based on project notifcation settings
@@ -353,7 +373,9 @@ class NotificationService
     users = users.reject(&:blocked?)
 
     users.reject do |user|
-      next user.notification_level == level unless project
+      global_notification_setting = user.global_notification_setting
+
+      next global_notification_setting.level == level unless project
 
       setting = user.notification_settings_for(project)
 
@@ -362,13 +384,13 @@ class NotificationService
       end
 
       # reject users who globally set mention notification and has no setting per project/group
-      next user.notification_level == level unless setting
+      next global_notification_setting.level == level unless setting
 
       # reject users who set mention notification in project
       next true if setting.level == level
 
       # reject users who have mention level in project and disabled in global settings
-      setting.global? && user.notification_level == level
+      setting.global? && global_notification_setting.level == level
     end
   end
 
@@ -457,7 +479,6 @@ class NotificationService
 
   def build_recipients(target, project, current_user, action: nil, previous_assignee: nil)
     recipients = target.participants(current_user)
-
     recipients = add_project_watchers(recipients, project)
     recipients = reject_mention_users(recipients, project)
 
diff --git a/app/services/oauth2/access_token_validation_service.rb b/app/services/oauth2/access_token_validation_service.rb
index 6194f6ce91eb334c30a14e9beba31cd63e4060ec..264fdccde8fdf2e286fabf274ccdd8e339daa9cb 100644
--- a/app/services/oauth2/access_token_validation_service.rb
+++ b/app/services/oauth2/access_token_validation_service.rb
@@ -22,6 +22,7 @@ module Oauth2::AccessTokenValidationService
     end
 
     protected
+
     # True if the token's scope is a superset of required scopes,
     # or the required scopes is empty.
     def sufficient_scope?(token, scopes)
diff --git a/app/services/path_locks/lock_service.rb b/app/services/path_locks/lock_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2c254c9e4cde29e2c731c286402af6b7e83a2ce2
--- /dev/null
+++ b/app/services/path_locks/lock_service.rb
@@ -0,0 +1,13 @@
+module PathLocks
+  class LockService < BaseService
+    AccessDenied = Class.new(StandardError)
+
+    include PathLocksHelper
+
+    def execute(path)
+      raise AccessDenied, 'You have no permissions' unless can?(current_user, :push_code, project)
+
+      project.path_locks.create(path: path, user: current_user)
+    end
+  end
+end
diff --git a/app/services/path_locks/unlock_service.rb b/app/services/path_locks/unlock_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b807368acf4d05cde75d212f3dcb76217cef21c8
--- /dev/null
+++ b/app/services/path_locks/unlock_service.rb
@@ -0,0 +1,13 @@
+module PathLocks
+  class UnlockService < BaseService
+    AccessDenied = Class.new(StandardError)
+
+    include PathLocksHelper
+
+    def execute(path_lock)
+      raise AccessDenied, 'You have no permissions' unless can_unlock?(path_lock)
+
+      path_lock.destroy
+    end
+  end
+end
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index ff52c03260a9e01fbea6460b8f7f741bc4b641d1..0b45a69e498578627d80dbbace84ec1fcbbc196d 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -56,14 +56,14 @@ module Projects
 
       after_create_actions if @project.persisted?
 
-      @project.add_import_job if @project.import?
-
+      if @project.errors.empty?
+        @project.add_import_job if @project.import?
+      else
+        fail(error: @project.errors.full_messages.join(', '))
+      end
       @project
     rescue => e
-      message = "Unable to save project: #{e.message}"
-      Rails.logger.error(message)
-      @project.errors.add(:base, message) if @project
-      @project
+      fail(error: e.message)
     end
 
     protected
@@ -110,5 +110,19 @@ module Projects
         end
       end
     end
+
+    def fail(error:)
+      message = "Unable to save project. Error: #{error}"
+      message << "Project ID: #{@project.id}" if @project && @project.id
+
+      Rails.logger.error(message)
+
+      if @project && @project.import?
+        @project.errors.add(:base, message)
+        @project.mark_import_as_failed(message)
+      end
+
+      @project
+    end
   end
 end
diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb
index 0577ae778d54d176f57494e0101bdf3d2c916cc5..de6dc38cc8e345f87c22445f4622a2a11086f349 100644
--- a/app/services/projects/fork_service.rb
+++ b/app/services/projects/fork_service.rb
@@ -3,7 +3,7 @@ module Projects
     def execute
       new_params = {
         forked_from_project_id: @project.id,
-        visibility_level:       @project.visibility_level,
+        visibility_level:       allowed_visibility_level,
         description:            @project.description,
         name:                   @project.name,
         path:                   @project.path,
@@ -19,5 +19,17 @@ module Projects
       new_project = CreateService.new(current_user, new_params).execute
       new_project
     end
+
+    private
+
+    def allowed_visibility_level
+      project_level = @project.visibility_level
+
+      if Gitlab::VisibilityLevel.non_restricted_level?(project_level)
+        project_level
+      else
+        Gitlab::VisibilityLevel.highest_allowed_level
+      end
+    end
   end
 end
diff --git a/app/services/projects/housekeeping_service.rb b/app/services/projects/housekeeping_service.rb
index 3b7c36f0908b61526836c6ef4a93ebad8a138326..43db29315a166cb3b2360765831a7c5e6220ff2d 100644
--- a/app/services/projects/housekeeping_service.rb
+++ b/app/services/projects/housekeeping_service.rb
@@ -22,7 +22,7 @@ module Projects
     end
 
     def execute
-      raise LeaseTaken if !try_obtain_lease
+      raise LeaseTaken unless try_obtain_lease
 
       GitlabShellOneShotWorker.perform_async(:gc, @project.path_with_namespace)
     ensure
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index ef15ef6a47310c2afc4db4b4ced7436d7a9617aa..c4838d31f2f64a50ff7677a316d4aa02ae529a69 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -39,7 +39,7 @@ module Projects
       begin
         gitlab_shell.import_repository(project.path_with_namespace, project.import_url)
       rescue Gitlab::Shell::Error => e
-        raise Error, e.message
+        raise Error,  "Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}"
       end
     end
 
diff --git a/app/services/projects/update_mirror_service.rb b/app/services/projects/update_mirror_service.rb
index e6742038166b14e1e118d8900f6dfb9347468d8d..aa750d5a799602d72718df48ad41d97a0877efb2 100644
--- a/app/services/projects/update_mirror_service.rb
+++ b/app/services/projects/update_mirror_service.rb
@@ -4,9 +4,11 @@ module Projects
     class UpdateError < Error; end
 
     def execute
-      return false unless project.mirror?
+      unless project.mirror?
+        return error("The project has no mirror to update")
+      end
 
-      unless current_user.can?(:push_code_to_protected_branches, project)
+      unless can?(current_user, :push_code_to_protected_branches, project)
         return error("The mirror user is not allowed to push code to all branches on this project.")
       end
 
diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb
index aab41f83d16d16c6024b0a28a015c1d1635d45f8..90fff91dd9cdc90f28ea63c6066cba1033f1ebdd 100644
--- a/app/services/projects/update_pages_service.rb
+++ b/app/services/projects/update_pages_service.rb
@@ -52,7 +52,7 @@ module Projects
     def create_status
       GenericCommitStatus.new(
         project: project,
-        commit: build.commit,
+        pipeline: build.pipeline,
         user: build.user,
         ref: build.ref,
         stage: 'deploy',
diff --git a/app/services/search/global_service.rb b/app/services/search/global_service.rb
index bc4478b5a44d9189005081c90657047a6b06d2a4..e5a935b2572f0b774bf5971987393fc4106ae9f1 100644
--- a/app/services/search/global_service.rb
+++ b/app/services/search/global_service.rb
@@ -1,5 +1,7 @@
 module Search
   class GlobalService
+    include Gitlab::CurrentSettings
+
     attr_accessor :current_user, :params
 
     def initialize(user, params)
@@ -11,7 +13,7 @@ module Search
       projects = ProjectsFinder.new.execute(current_user)
       projects = projects.in_namespace(group.id) if group
 
-      if Gitlab.config.elasticsearch.enabled
+      if current_application_settings.elasticsearch_search?
         Gitlab::Elastic::SearchResults.new(current_user, projects.pluck(:id), params[:search])
       else
         Gitlab::SearchResults.new(current_user, projects, params[:search])
diff --git a/app/services/search/project_service.rb b/app/services/search/project_service.rb
index 0531c046ba0acac92504a38fdf91ddb45922eea5..cb1d33d6dd0bf8acbf88789a40e3fa27d88038fc 100644
--- a/app/services/search/project_service.rb
+++ b/app/services/search/project_service.rb
@@ -1,5 +1,7 @@
 module Search
   class ProjectService
+    include Gitlab::CurrentSettings
+
     attr_accessor :project, :current_user, :params
 
     def initialize(project, user, params)
@@ -7,7 +9,7 @@ module Search
     end
 
     def execute
-      if Gitlab.config.elasticsearch.enabled
+      if current_application_settings.elasticsearch_search?
         Gitlab::Elastic::ProjectSearchResults.new(current_user,
                                                   project.id,
                                                   params[:search],
diff --git a/app/services/search/snippet_service.rb b/app/services/search/snippet_service.rb
index a474b395939dd995e4e0a397221c6cbadd20c5cd..b30326425cb29eb68bb13bba3e62f38ad14de77b 100644
--- a/app/services/search/snippet_service.rb
+++ b/app/services/search/snippet_service.rb
@@ -1,5 +1,6 @@
 module Search
   class SnippetService
+    include Gitlab::CurrentSettings
     attr_accessor :current_user, :params
 
     def initialize(user, params)
@@ -7,7 +8,7 @@ module Search
     end
 
     def execute
-      if Gitlab.config.elasticsearch.enabled
+      if current_application_settings.elasticsearch_search?
         Gitlab::Elastic::SnippetSearchResults.new(current_user,
                                                   params[:search])
       else
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index 4bf4e144727c543770e4676a70836914e4f04d85..e1f9ea64dc49e0690b0b4f578a29a4a1ad111f37 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -20,7 +20,7 @@ class TodoService
   #  * mark all pending todos related to the issue for the current user as done
   #
   def update_issue(issue, current_user)
-    create_mention_todos(issue.project, issue, current_user)
+    update_issuable(issue, current_user)
   end
 
   # When close an issue we should:
@@ -53,7 +53,7 @@ class TodoService
   #  * create a todo for each mentioned user on merge request
   #
   def update_merge_request(merge_request, current_user)
-    create_mention_todos(merge_request.project, merge_request, current_user)
+    update_issuable(merge_request, current_user)
   end
 
   # When close a merge request we should:
@@ -122,6 +122,14 @@ class TodoService
     handle_note(note, current_user)
   end
 
+  # When an emoji is awarded we should:
+  #
+  #  * mark all pending todos related to the awardable for the current user as done
+  #
+  def new_award_emoji(awardable, current_user)
+    mark_pending_todos_as_done(awardable, current_user)
+  end
+
   # When marking pending todos as done we should:
   #
   #  * mark all pending todos related to the target for the current user as done
@@ -131,10 +139,16 @@ class TodoService
     pending_todos(user, attributes).update_all(state: :done)
   end
 
+  # When user marks an issue as todo
+  def mark_todo(issuable, current_user)
+    attributes = attributes_for_todo(issuable.project, issuable, current_user, Todo::MARKED)
+    create_todos(current_user, attributes)
+  end
+
   private
 
   def create_todos(users, attributes)
-    Array(users).each do |user|
+    Array(users).map do |user|
       next if pending_todos(user, attributes).exists?
       Todo.create(attributes.merge(user_id: user.id))
     end
@@ -145,6 +159,13 @@ class TodoService
     create_mention_todos(issuable.project, issuable, author)
   end
 
+  def update_issuable(issuable, author)
+    # Skip toggling a task list item in a description
+    return if issuable.tasks? && issuable.updated_tasks.any?
+
+    create_mention_todos(issuable.project, issuable, author)
+  end
+
   def handle_note(note, author)
     # Skip system notes, and notes on project snippet
     return if note.system? || note.for_snippet?
diff --git a/app/services/wiki_pages/base_service.rb b/app/services/wiki_pages/base_service.rb
index 9162f1286026c7f071f0a8d6054739556db22f4b..4c0a2c6b4d834c744a69bb0759bb010a5403d2d3 100644
--- a/app/services/wiki_pages/base_service.rb
+++ b/app/services/wiki_pages/base_service.rb
@@ -6,9 +6,8 @@ module WikiPages
         object_kind: page.class.name.underscore,
         user: current_user.hook_attrs,
         project: @project.hook_attrs,
-        object_attributes: page.hook_attrs,
-        # DEPRECATED
-        repository: @project.hook_attrs.slice(:name, :url, :description, :homepage)
+        wiki: @project.wiki.hook_attrs,
+        object_attributes: page.hook_attrs
       }
 
       page_url = Gitlab::UrlBuilder.build(page)
diff --git a/app/views/admin/abuse_reports/_abuse_report.html.haml b/app/views/admin/abuse_reports/_abuse_report.html.haml
index 2ab01704b7768cbc0d8c8e61a6a491f81620281c..862b86d9d4a8be2fa94828069eb337c098ed6e97 100644
--- a/app/views/admin/abuse_reports/_abuse_report.html.haml
+++ b/app/views/admin/abuse_reports/_abuse_report.html.haml
@@ -16,7 +16,7 @@
     .light.small
       = time_ago_with_tooltip(abuse_report.created_at)
   %td
-    = markdown(abuse_report.message.squish!, pipeline: :single_line)
+    = markdown(abuse_report.message.squish!, pipeline: :single_line, author: reporter)
   %td
     - if user
       = link_to 'Remove user & report', admin_abuse_report_path(abuse_report, remove_user: true),
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index 9d07b616cced084d67e26d6f7a40b6d959b80bce..746e99b5fd0e25343a2262082633a9fa02e25592 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -159,6 +159,10 @@
       .col-sm-10
         = f.text_area :help_text, class: 'form-control', rows: 4
         .help-block Markdown enabled
+    .form-group
+      = f.label :after_sign_up_text, class: 'control-label col-sm-2'
+      .col-sm-10
+        = f.text_area :after_sign_up_text, class: 'form-control', rows: 4
     .form-group
       = f.label :help_page_text, class: 'control-label col-sm-2'
       .col-sm-10
@@ -191,6 +195,14 @@
       .col-sm-10
         = f.number_field :max_artifacts_size, class: 'form-control'
 
+  - if Gitlab.config.registry.enabled
+    %fieldset
+      %legend Container Registry
+      .form-group
+        = f.label :container_registry_token_expire_delay, 'Authorization token duration (minutes)', class: 'control-label col-sm-2'
+        .col-sm-10
+          = f.number_field :container_registry_token_expire_delay, class: 'form-control'
+
   %fieldset
     %legend Metrics
     %p
@@ -328,6 +340,34 @@
         .help-block
           If you got a lot of false alarms from repository checks you can choose to clear all repository check information from the database.
 
+  %fieldset
+    %legend Elasticsearch
+    .form-group
+      .col-sm-offset-2.col-sm-10
+        .checkbox
+          = f.label :elasticsearch_indexing do
+            = f.check_box :elasticsearch_indexing
+            Elasticsearch indexing
+
+    .form-group
+      .col-sm-offset-2.col-sm-10
+        .checkbox
+          = f.label :elasticsearch_search do
+            = f.check_box :elasticsearch_search
+            Search with Elasticsearch enabled
+
+    .form-group
+      = f.label :elasticsearch_host, 'Host', class: 'control-label col-sm-2'
+      .col-sm-10
+        = f.text_field :elasticsearch_host, value: @application_setting.elasticsearch_host.join(', '), class: 'form-control', placeholder: 'localhost'
+        .help-block
+          The TCP/IP host to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., "host1, host2").
+
+    .form-group
+      = f.label :elasticsearch_port, 'Port', class: 'control-label col-sm-2'
+      .col-sm-10
+        = f.text_field :elasticsearch_port, class: 'form-control', placeholder: ApplicationSetting.current.elasticsearch_port
+
 
   .form-actions
     = f.submit 'Save', class: 'btn btn-save'
diff --git a/app/views/admin/background_jobs/_head.html.haml b/app/views/admin/background_jobs/_head.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..d78682532ed29c74f89d10387d9cc8cd39a4753a
--- /dev/null
+++ b/app/views/admin/background_jobs/_head.html.haml
@@ -0,0 +1,14 @@
+.nav-links.sub-nav
+  %ul{ class: (container_class) }
+    = nav_link(controller: :background_jobs) do
+      = link_to admin_background_jobs_path, title: 'Background Jobs' do
+        %span
+          Background Jobs
+    = nav_link(controller: :logs) do
+      = link_to admin_logs_path, title: 'Logs' do
+        %span
+          Logs
+    = nav_link(controller: :health_check) do
+      = link_to admin_health_check_path, title: 'Health Check' do
+        %span
+          Health Check
diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml
index de5bc050cf0b409dc8ce203efd3a206a198bc369..654d261aa997712ba25441fe84508af1ad453ea0 100644
--- a/app/views/admin/background_jobs/show.html.haml
+++ b/app/views/admin/background_jobs/show.html.haml
@@ -1,46 +1,50 @@
+- @no_container = true
 - page_title "Background Jobs"
-%h3.page-title Background Jobs
-%p.light GitLab uses #{link_to "sidekiq", "http://sidekiq.org/"} library for async job processing
+= render 'admin/background_jobs/head'
 
-%hr
+%div{ class: (container_class) }
+  %h3.page-title Background Jobs
+  %p.light GitLab uses #{link_to "sidekiq", "http://sidekiq.org/"} library for async job processing
 
-.panel.panel-default
-  .panel-heading Sidekiq running processes
-  .panel-body
-    - if @sidekiq_processes.empty?
-      %h4.cred
-        %i.fa.fa-exclamation-triangle
-        There are no running sidekiq processes. Please restart GitLab
-    - else
-      .table-holder
-        %table.table
-          %thead
-            %th USER
-            %th PID
-            %th CPU
-            %th MEM
-            %th STATE
-            %th START
-            %th COMMAND
-          %tbody
-            - @sidekiq_processes.each do |process|
-              - next unless process.match(/(sidekiq \d+\.\d+\.\d+.+$)/)
-              - data = process.strip.split(' ')
-              %tr
-                %td= gitlab_config.user
-                - 5.times do
-                  %td= data.shift
-                %td= data.join(' ')
+  %hr
 
-      .clearfix
-        %p
-          %i.fa.fa-exclamation-circle
-          If '[25 of 25 busy]' is shown, restart GitLab with 'sudo service gitlab reload'.
-        %p
-          %i.fa.fa-exclamation-circle
-          If more than one sidekiq process is listed, stop GitLab, kill the remaining sidekiq processes (sudo pkill -u #{gitlab_config.user} -f sidekiq) and restart GitLab.
+  .panel.panel-default
+    .panel-heading Sidekiq running processes
+    .panel-body
+      - if @sidekiq_processes.empty?
+        %h4.cred
+          %i.fa.fa-exclamation-triangle
+          There are no running sidekiq processes. Please restart GitLab
+      - else
+        .table-holder
+          %table.table
+            %thead
+              %th USER
+              %th PID
+              %th CPU
+              %th MEM
+              %th STATE
+              %th START
+              %th COMMAND
+            %tbody
+              - @sidekiq_processes.each do |process|
+                - next unless process.match(/(sidekiq \d+\.\d+\.\d+.+$)/)
+                - data = process.strip.split(' ')
+                %tr
+                  %td= gitlab_config.user
+                  - 5.times do
+                    %td= data.shift
+                  %td= data.join(' ')
 
+        .clearfix
+          %p
+            %i.fa.fa-exclamation-circle
+            If '[25 of 25 busy]' is shown, restart GitLab with 'sudo service gitlab reload'.
+          %p
+            %i.fa.fa-exclamation-circle
+            If more than one sidekiq process is listed, stop GitLab, kill the remaining sidekiq processes (sudo pkill -u #{gitlab_config.user} -f sidekiq) and restart GitLab.
 
 
-.panel.panel-default
-  %iframe{src: sidekiq_path, width: '100%', height: 970, style: "border: none"}
+
+  .panel.panel-default
+    %iframe{src: sidekiq_path, width: '100%', height: 970, style: "border: none"}
diff --git a/app/views/admin/builds/index.html.haml b/app/views/admin/builds/index.html.haml
index ed24757087b9446484ee56c5e8326d665cffc01f..efd5b12cfeb4b182be40679949312c992ad1c9c8 100644
--- a/app/views/admin/builds/index.html.haml
+++ b/app/views/admin/builds/index.html.haml
@@ -1,50 +1,54 @@
-.top-area
-  %ul.nav-links
-    %li{class: ('active' if @scope.nil?)}
-      = link_to admin_builds_path do
-        All
-        %span.badge.js-totalbuilds-count= @all_builds.count(:id)
-
-    %li{class: ('active' if @scope == 'running')}
-      = link_to admin_builds_path(scope: :running) do
-        Running
-        %span.badge.js-running-count= number_with_delimiter(@all_builds.running_or_pending.count(:id))
-
-    %li{class: ('active' if @scope == 'finished')}
-      = link_to admin_builds_path(scope: :finished) do
-        Finished
-        %span.badge.js-running-count= number_with_delimiter(@all_builds.finished.count(:id))
-
-  .nav-controls
-    - 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
-
-.row-content-block.second-block
-  #{(@scope || 'all').capitalize} builds
-
-%ul.content-list
-  - if @builds.blank?
-    %li
-      .nothing-here-block No builds to show
-  - else
-    .table-holder
-      %table.table.builds
-        %thead
-          %tr
-            %th Status
-            %th Build ID
-            %th Project
-            %th Commit
-            %th Ref
-            %th Runner
-            %th Name
-            %th Tags
-            %th Duration
-            %th Finished at
-            %th
-
-        - @builds.each do |build|
-          = render "admin/builds/build", build: build
-
-    = paginate @builds, theme: 'gitlab'
-
+- @no_container = true
+= render "admin/dashboard/head"
+
+%div{ class: (container_class) }
+
+  .top-area
+    %ul.nav-links
+      %li{class: ('active' if @scope.nil?)}
+        = link_to admin_builds_path do
+          All
+          %span.badge.js-totalbuilds-count= @all_builds.count(:id)
+
+      %li{class: ('active' if @scope == 'running')}
+        = link_to admin_builds_path(scope: :running) do
+          Running
+          %span.badge.js-running-count= number_with_delimiter(@all_builds.running_or_pending.count(:id))
+
+      %li{class: ('active' if @scope == 'finished')}
+        = link_to admin_builds_path(scope: :finished) do
+          Finished
+          %span.badge.js-running-count= number_with_delimiter(@all_builds.finished.count(:id))
+
+    .nav-controls
+      - 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
+
+  .row-content-block.second-block
+    #{(@scope || 'all').capitalize} builds
+
+  %ul.content-list
+    - if @builds.blank?
+      %li
+        .nothing-here-block No builds to show
+    - else
+      .table-holder
+        %table.table.builds
+          %thead
+            %tr
+              %th Status
+              %th Build ID
+              %th Project
+              %th Commit
+              %th Ref
+              %th Runner
+              %th Name
+              %th Tags
+              %th Duration
+              %th Finished at
+              %th
+
+          - @builds.each do |build|
+            = render "admin/builds/build", build: build
+
+      = paginate @builds, theme: 'gitlab'
diff --git a/app/views/admin/dashboard/_head.html.haml b/app/views/admin/dashboard/_head.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..7b3f88c24df7dc5893d7eca650d453dc0fb285c6
--- /dev/null
+++ b/app/views/admin/dashboard/_head.html.haml
@@ -0,0 +1,22 @@
+.nav-links.sub-nav
+  %ul{ class: (container_class) }
+    = nav_link(controller: :dashboard, html_options: {class: 'home'}) do
+      = link_to admin_root_path, title: 'Overview' do
+        %span
+          Overview
+    = nav_link(controller: [:admin, :projects]) do
+      = link_to admin_namespaces_projects_path, title: 'Projects' do
+        %span
+          Projects
+    = nav_link(controller: :users) do
+      = link_to admin_users_path, title: 'Users' do
+        %span
+          Users
+    = nav_link(controller: :groups) do
+      = link_to admin_groups_path, title: 'Groups' do
+        %span
+          Groups
+    = nav_link path: 'builds#index' do
+      = link_to admin_builds_path, title: 'Builds' do
+        %span
+          Builds
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index d9f04e1fff6af9fd96cf39c52d455da9632490ec..4beea2a4947bf4c69d5fd55310c88da03813bef0 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -1,159 +1,172 @@
-.admin-dashboard.prepend-top-default
-  .row
-    .col-md-4
-      %h4 Statistics
-      %hr
-      %p
-        Forks
-        %span.light.pull-right
-          = number_with_delimiter(ForkedProjectLink.count)
-      %p
-        Issues
-        %span.light.pull-right
-          = number_with_delimiter(Issue.count)
-      %p
-        Merge Requests
-        %span.light.pull-right
-          = number_with_delimiter(MergeRequest.count)
-      %p
-        Notes
-        %span.light.pull-right
-          = number_with_delimiter(Note.count)
-      %p
-        Snippets
-        %span.light.pull-right
-          = number_with_delimiter(Snippet.count)
-      %p
-        SSH Keys
-        %span.light.pull-right
-          = number_with_delimiter(Key.count)
-      %p
-        Milestones
-        %span.light.pull-right
-          = number_with_delimiter(Milestone.count)
-      %p
-        Active Users
-        %span.light.pull-right
-          = number_with_delimiter(User.active.count)
-    .col-md-4
-      %h4
-        Features
-      %hr
-      %p
-        Sign up
-        %span.light.pull-right
-          = boolean_to_icon signup_enabled?
-      %p
-        LDAP
-        %span.light.pull-right
-          = boolean_to_icon Gitlab.config.ldap.enabled
-      %p
-        Gravatar
-        %span.light.pull-right
-          = boolean_to_icon gravatar_enabled?
-      %p
-        OmniAuth
-        %span.light.pull-right
-          = boolean_to_icon Gitlab.config.omniauth.enabled
-      %p
-        Reply by email
-        %span.light.pull-right
-          = boolean_to_icon Gitlab::IncomingEmail.enabled?
-      %p
-        Elasticsearch
-        %span.light.pull-right
-          = boolean_to_icon Gitlab.config.elasticsearch.enabled
-    .col-md-4
-      %h4
-        Components
-        - if current_application_settings.version_check_enabled
-          .pull-right
-            = version_status_badge
+- @no_container = true
+= render "admin/dashboard/head"
 
-      %hr
-      %p
-        GitLab
-        %span.pull-right
-          = Gitlab::VERSION
-      %p
-        GitLab Shell
-        %span.pull-right
-          = Gitlab::Shell.new.version
-      %p
-        GitLab API
-        %span.pull-right
-          = API::API::version
-      %p
-        Git
-        %span.pull-right
-          = Gitlab::Git.version
-      %p
-        Ruby
-        %span.pull-right
-          #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
-
-      %p
-        Rails
-        %span.pull-right
-          #{Rails::VERSION::STRING}
-
-      %p
-        = Gitlab::Database.adapter_name
-        %span.pull-right
-          = Gitlab::Database.version
-  %hr
-  .row
-    .col-sm-4
-      .light-well
-        %h4 Projects
-        .data
-          = link_to admin_namespaces_projects_path do
-            %h1= number_with_delimiter(Project.count)
-          %hr
-          = link_to('New Project', new_project_path, class: "btn btn-new")
-    .col-sm-4
-      .light-well
-        %h4 Users
-        .data
-          = link_to admin_users_path do
-            %h1= number_with_delimiter(User.count)
-          %hr
-          = link_to 'New User', new_admin_user_path, class: "btn btn-new"
-    .col-sm-4
-      .light-well
-        %h4 Groups
-        .data
-          = link_to admin_groups_path do
-            %h1= number_with_delimiter(Group.count)
-          %hr
-          = link_to 'New Group', new_admin_group_path, class: "btn btn-new"
-
-  .row.prepend-top-10
-    .col-md-4
-      %h4 Latest projects
-      %hr
-      - @projects.each do |project|
+%div{ class: (container_class) }
+  .admin-dashboard.prepend-top-default
+    .row
+      .col-md-4
+        %h4 Statistics
+        %hr
         %p
-          = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project], class: 'str-truncated'
+          Forks
           %span.light.pull-right
-            #{time_ago_with_tooltip(project.created_at)}
-
-    .col-md-4
-      %h4 Latest users
-      %hr
-      - @users.each do |user|
+            = number_with_delimiter(ForkedProjectLink.count)
         %p
-          = link_to [:admin, user], class: 'str-truncated' do
-            = user.name
+          Issues
           %span.light.pull-right
-            #{time_ago_with_tooltip(user.created_at)}
-
-    .col-md-4
-      %h4 Latest groups
-      %hr
-      - @groups.each do |group|
+            = number_with_delimiter(Issue.count)
+        %p
+          Merge Requests
+          %span.light.pull-right
+            = number_with_delimiter(MergeRequest.count)
+        %p
+          Notes
+          %span.light.pull-right
+            = number_with_delimiter(Note.count)
+        %p
+          Snippets
+          %span.light.pull-right
+            = number_with_delimiter(Snippet.count)
+        %p
+          SSH Keys
+          %span.light.pull-right
+            = number_with_delimiter(Key.count)
+        %p
+          Milestones
+          %span.light.pull-right
+            = number_with_delimiter(Milestone.count)
+        %p
+          Active Users
+          %span.light.pull-right
+            = number_with_delimiter(User.active.count)
+      .col-md-4
+        %h4
+          Features
+        %hr
+        %p
+          Sign up
+          %span.light.pull-right
+            = boolean_to_icon signup_enabled?
+        %p
+          LDAP
+          %span.light.pull-right
+            = boolean_to_icon Gitlab.config.ldap.enabled
         %p
-          = link_to [:admin, group], class: 'str-truncated' do
-            = group.name
+          Gravatar
           %span.light.pull-right
-            #{time_ago_with_tooltip(group.created_at)}
+            = boolean_to_icon gravatar_enabled?
+        %p
+          OmniAuth
+          %span.light.pull-right
+            = boolean_to_icon Gitlab.config.omniauth.enabled
+        %p
+          Reply by email
+          %span.light.pull-right
+            = boolean_to_icon Gitlab::IncomingEmail.enabled?
+        %p
+          Elasticsearch
+          %span.light.pull-right
+            = boolean_to_icon current_application_settings.elasticsearch_search?
+        %p
+          Geo
+          %span.light.pull-right
+            = boolean_to_icon Gitlab::Geo.enabled?
+      .col-md-4
+        %h4
+          Components
+          - if current_application_settings.version_check_enabled
+            .pull-right
+              = version_status_badge
+
+        %hr
+        %p
+          GitLab
+          %span.pull-right
+            = Gitlab::VERSION
+        %p
+          GitLab Shell
+          %span.pull-right
+            = Gitlab::Shell.new.version
+        %p
+          GitLab API
+          %span.pull-right
+            = API::API::version
+        - if Gitlab::Geo.enabled?
+          %p
+            Geo
+            %span.pull-right
+              = Gitlab::Geo.current_node.primary ? 'Primary node' : 'Secondary node'
+        %p
+          Git
+          %span.pull-right
+            = Gitlab::Git.version
+        %p
+          Ruby
+          %span.pull-right
+            #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
+
+        %p
+          Rails
+          %span.pull-right
+            #{Rails::VERSION::STRING}
+
+        %p
+          = Gitlab::Database.adapter_name
+          %span.pull-right
+            = Gitlab::Database.version
+    %hr
+    .row
+      .col-sm-4
+        .light-well
+          %h4 Projects
+          .data
+            = link_to admin_namespaces_projects_path do
+              %h1= number_with_delimiter(Project.count)
+            %hr
+            = link_to('New Project', new_project_path, class: "btn btn-new")
+      .col-sm-4
+        .light-well
+          %h4 Users
+          .data
+            = link_to admin_users_path do
+              %h1= number_with_delimiter(User.count)
+            %hr
+            = link_to 'New User', new_admin_user_path, class: "btn btn-new"
+      .col-sm-4
+        .light-well
+          %h4 Groups
+          .data
+            = link_to admin_groups_path do
+              %h1= number_with_delimiter(Group.count)
+            %hr
+            = link_to 'New Group', new_admin_group_path, class: "btn btn-new"
+
+    .row.prepend-top-10
+      .col-md-4
+        %h4 Latest projects
+        %hr
+        - @projects.each do |project|
+          %p
+            = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project], class: 'str-truncated'
+            %span.light.pull-right
+              #{time_ago_with_tooltip(project.created_at)}
+
+      .col-md-4
+        %h4 Latest users
+        %hr
+        - @users.each do |user|
+          %p
+            = link_to [:admin, user], class: 'str-truncated' do
+              = user.name
+            %span.light.pull-right
+              #{time_ago_with_tooltip(user.created_at)}
+
+      .col-md-4
+        %h4 Latest groups
+        %hr
+        - @groups.each do |group|
+          %p
+            = link_to [:admin, group], class: 'str-truncated' do
+              = group.name
+            %span.light.pull-right
+              #{time_ago_with_tooltip(group.created_at)}
diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml
index 775072a744136b18012d00957c10c6287407d3bc..4f1996ef7ab26de3b261d36ba04aac4540df1f4a 100644
--- a/app/views/admin/groups/index.html.haml
+++ b/app/views/admin/groups/index.html.haml
@@ -1,41 +1,45 @@
+- @no_container = true
 - page_title "Groups"
-%h3.page-title
-  Groups (#{number_with_delimiter(@groups.total_count)})
+= render "admin/dashboard/head"
 
-%p.light
-  Group allows you to keep projects organized.
-  Use groups for uniting related projects.
+%div{ class: (container_class) }
+  %h3.page-title
+    Groups (#{number_with_delimiter(@groups.total_count)})
 
-.top-area
-  .nav-search
-    = form_tag admin_groups_path, method: :get, class: 'form-inline' do
-      = hidden_field_tag :sort, @sort
-      = text_field_tag :name, params[:name], class: "form-control"
-      = button_tag "Search", class: "btn submit btn-primary"
+  %p.light
+    Group allows you to keep projects organized.
+    Use groups for uniting related projects.
 
-  .nav-controls
-    .dropdown.inline
-      %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
-        %span.light
-        - if @sort.present?
-          = sort_options_hash[@sort]
-        - else
-          = sort_title_recently_created
-        %b.caret
-      %ul.dropdown-menu
-        %li
-          = link_to admin_groups_path(sort: sort_value_recently_created) do
+  .top-area
+    .nav-search
+      = form_tag admin_groups_path, method: :get, class: 'form-inline' do
+        = hidden_field_tag :sort, @sort
+        = text_field_tag :name, params[:name], class: "form-control"
+        = button_tag "Search", class: "btn submit btn-primary"
+
+    .nav-controls
+      .dropdown.inline
+        %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+          %span.light
+          - if @sort.present?
+            = sort_options_hash[@sort]
+          - else
             = sort_title_recently_created
-          = link_to admin_groups_path(sort: sort_value_oldest_created) do
-            = sort_title_oldest_created
-          = link_to admin_groups_path(sort: sort_value_recently_updated) do
-            = sort_title_recently_updated
-          = link_to admin_groups_path(sort: sort_value_oldest_updated) do
-            = sort_title_oldest_updated
-    = link_to 'New Group', new_admin_group_path, class: "btn btn-new"
+          %b.caret
+        %ul.dropdown-menu
+          %li
+            = link_to admin_groups_path(sort: sort_value_recently_created) do
+              = sort_title_recently_created
+            = link_to admin_groups_path(sort: sort_value_oldest_created) do
+              = sort_title_oldest_created
+            = link_to admin_groups_path(sort: sort_value_recently_updated) do
+              = sort_title_recently_updated
+            = link_to admin_groups_path(sort: sort_value_oldest_updated) do
+              = sort_title_oldest_updated
+      = link_to 'New Group', new_admin_group_path, class: "btn btn-new"
 
-%ul.content-list
-  - @groups.each do |group|
-    = render 'group', group: group
+  %ul.content-list
+    - @groups.each do |group|
+      = render 'group', group: group
 
-= paginate @groups, theme: "gitlab"
+  = paginate @groups, theme: "gitlab"
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 48f4f8f8278b713329e1287d0996cb29542498d6..56347d27a791065a4e7d6052b7e2cea693bcd874 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -120,7 +120,7 @@
             %span.pull-right.light
               = member.human_access
               - if can?(current_user, :destroy_group_member, member)
-                = link_to group_group_member_path(@group, member), data: { confirm: remove_user_from_group_message(@group, member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do
+                = link_to group_group_member_path(@group, member), data: { confirm: remove_member_message(member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do
                   %i.fa.fa-minus.fa-inverse
       .panel-footer
         = paginate @members, param_name: 'members_page', theme: 'gitlab'
diff --git a/app/views/admin/health_check/show.html.haml b/app/views/admin/health_check/show.html.haml
index c2313986a7fefadc8d0de3dd6110633eea8a6fa2..7b8407f9152ea221deb516a4d10be2c442e59da0 100644
--- a/app/views/admin/health_check/show.html.haml
+++ b/app/views/admin/health_check/show.html.haml
@@ -1,49 +1,52 @@
+- @no_container = true
 - page_title "Health Check"
+= render 'admin/background_jobs/head'
 
-%h3.page-title
-  Health Check
-.bs-callout.clearfix
-  .pull-left
-    %p
-  Access token is
-  %code#health-check-token= current_application_settings.health_check_access_token
-  = button_to reset_health_check_token_admin_application_settings_path,
-    method: :put, class: 'btn btn-default',
-    data: { confirm: 'Are you sure you want to reset the health check token?' } do
-    = icon('refresh')
-    Reset health check access token
-%p.light
-  Health information can be retrieved as plain text, JSON, or XML using:
-  %ul
-    %li
-      %code= health_check_url(token: current_application_settings.health_check_access_token)
-    %li
-      %code= health_check_url(token: current_application_settings.health_check_access_token, format: :json)
-    %li
-      %code= health_check_url(token: current_application_settings.health_check_access_token, format: :xml)
+%div{ class: (container_class) }
+  %h3.page-title
+    Health Check
+  .bs-callout.clearfix
+    .pull-left
+      %p
+    Access token is
+    %code#health-check-token= current_application_settings.health_check_access_token
+    = button_to reset_health_check_token_admin_application_settings_path,
+      method: :put, class: 'btn btn-default',
+      data: { confirm: 'Are you sure you want to reset the health check token?' } do
+      = icon('refresh')
+      Reset health check access token
+  %p.light
+    Health information can be retrieved as plain text, JSON, or XML using:
+    %ul
+      %li
+        %code= health_check_url(token: current_application_settings.health_check_access_token)
+      %li
+        %code= health_check_url(token: current_application_settings.health_check_access_token, format: :json)
+      %li
+        %code= health_check_url(token: current_application_settings.health_check_access_token, format: :xml)
 
-%p.light
-  You can also ask for the status of specific services:
-  %ul
-    %li
-      %code= health_check_url(token: current_application_settings.health_check_access_token, checks: :cache)
-    %li
-      %code= health_check_url(token: current_application_settings.health_check_access_token, checks: :database)
-    %li
-      %code= health_check_url(token: current_application_settings.health_check_access_token, checks: :migrations)
+  %p.light
+    You can also ask for the status of specific services:
+    %ul
+      %li
+        %code= health_check_url(token: current_application_settings.health_check_access_token, checks: :cache)
+      %li
+        %code= health_check_url(token: current_application_settings.health_check_access_token, checks: :database)
+      %li
+        %code= health_check_url(token: current_application_settings.health_check_access_token, checks: :migrations)
 
-%hr
-.panel.panel-default
-  .panel-heading
-    Current Status:
-    - if @errors.blank?
-      = icon('circle', class: 'cgreen')
-      Healthy
-    - else
-      = icon('warning', class: 'cred')
-      Unhealthy
-  .panel-body
-    - if @errors.blank?
-      No Health Problems Detected
-    - else
-      = @errors
+  %hr
+  .panel.panel-default
+    .panel-heading
+      Current Status:
+      - if @errors.blank?
+        = icon('circle', class: 'cgreen')
+        Healthy
+      - else
+        = icon('warning', class: 'cred')
+        Unhealthy
+    .panel-body
+      - if @errors.blank?
+        No Health Problems Detected
+      - else
+        = @errors
diff --git a/app/views/admin/licenses/show.html.haml b/app/views/admin/licenses/show.html.haml
index 6e28fbc22f01d202f310e781b4cb4f399c625f48..702ef12a388c123339175ed39e9bac4d3e9b5434 100644
--- a/app/views/admin/licenses/show.html.haml
+++ b/app/views/admin/licenses/show.html.haml
@@ -1,5 +1,5 @@
 - page_title "License"
-%h3.page-title 
+%h3.page-title
   Your License
   = link_to 'Upload New License', new_admin_license_path, class: "btn btn-new pull-right"
 
@@ -27,7 +27,7 @@
           %span.light Started:
           %strong= time_ago_with_tooltip @license.starts_at
         %li
-          %span.light 
+          %span.light
             - if @license.expired?
               Expired:
             - else
@@ -62,12 +62,12 @@
 
           - if restricted && current > restricted
             %span.label.label-danger.pull-right
-              %strong 
+              %strong
                 Exceeds license limit
 
         - date_range = (Date.today - 1.year)..Date.today
         - historical = HistoricalData.during(date_range).maximum(:active_user_count)
-        - if historical 
+        - if historical
           %li
             %span.light Maximum active users:
             %strong
@@ -75,7 +75,7 @@
 
             - if restricted && historical > restricted
               %span.label.label-danger.pull-right
-                %strong 
+                %strong
                   Exceeds license limit
 
   .col-md-6
@@ -84,7 +84,7 @@
         Download license
       .panel-body
         %p Your license will be included in your GitLab backup and will survive upgrades, so in normal usage you should never need to re-upload your <code>.gitlab-license</code> file.
-        %p Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab B.V. to send it to you again.
+        %p Still, we recommend keeping a backup saved somewhere. Otherwise, if you ever need it and have lost it, you will need to request GitLab Inc. to send it to you again.
         %br
         = link_to 'Download license', download_admin_license_path, class: "btn btn-info"
 
diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml
index 698feb571ac822fcfdcef13bfc632263daa3c1f4..5ddc3b9ea85111de9f622ecbf53f3575da5cde04 100644
--- a/app/views/admin/logs/show.html.haml
+++ b/app/views/admin/logs/show.html.haml
@@ -1,28 +1,32 @@
+- @no_container = true
 - page_title "Logs"
 - loggers = [Gitlab::GitLogger, Gitlab::AppLogger,
              Gitlab::ProductionLogger, Gitlab::SidekiqLogger,
              Gitlab::RepositoryCheckLogger]
-%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'
-.row-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' : ''),
-        id: klass::file_name_noext }
-      .file-holder#README
-        .file-title
-          %i.fa.fa-file
-          = klass::file_name
-          .pull-right
-            = link_to '#', class: 'log-bottom' do
-              %i.fa.fa-arrow-down
-              Scroll down
-        .file-content.logs
-          %ol
-            - klass.read_latest.each do |line|
-              %li
-                %p= line
+= render 'admin/background_jobs/head'
+
+%div{ class: (container_class) }
+  %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'
+  .row-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' : ''),
+          id: klass::file_name_noext }
+        .file-holder#README
+          .file-title
+            %i.fa.fa-file
+            = klass::file_name
+            .pull-right
+              = link_to '#', class: 'log-bottom' do
+                %i.fa.fa-arrow-down
+                Scroll down
+          .file-content.logs
+            %ol
+              - klass.read_latest.each do |line|
+                %li
+                  %p= line
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index aa07afa0d62ccb3164117277668510f8b41626db..4822cb693c2d2b2a99b8fe1ea67bee5b37a8a3a2 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -1,94 +1,97 @@
+- @no_container = true
 - page_title "Projects"
 = render 'shared/show_aside'
+= render "admin/dashboard/head"
 
-.row.prepend-top-default
-  %aside.col-md-3
-    .panel.admin-filter
-      = form_tag admin_namespaces_projects_path, method: :get, class: '' do
-        .form-group
-          = label_tag :name, 'Name:'
-          = text_field_tag :name, params[:name], class: "form-control"
+%div{ class: (container_class) }
+  .row.prepend-top-default
+    %aside.col-md-3
+      .panel.admin-filter
+        = form_tag admin_namespaces_projects_path, method: :get, class: '' do
+          .form-group
+            = label_tag :name, 'Name:'
+            = text_field_tag :name, params[:name], class: "form-control"
 
-        .form-group
-          = label_tag :namespace_id, "Namespace"
-          = namespace_select_tag :namespace_id, selected: params[:namespace_id], class: 'input-large'
+          .form-group
+            = label_tag :namespace_id, "Namespace"
+            = namespace_select_tag :namespace_id, selected: params[:namespace_id], class: 'input-large'
 
-        .form-group
-          %strong Activity
-          .checkbox
-            = label_tag :with_push do
-              = check_box_tag :with_push, 1, params[:with_push]
-              %span Projects with push events
-          .checkbox
-            = label_tag :abandoned do
-              = check_box_tag :abandoned, 1, params[:abandoned]
-              %span No activity over 6 month
-          .checkbox
-            = label_tag :with_archived do
-              = check_box_tag :with_archived, 1, params[:with_archived]
-              %span Show archived projects
+          .form-group
+            %strong Activity
+            .checkbox
+              = label_tag :with_push do
+                = check_box_tag :with_push, 1, params[:with_push]
+                %span Projects with push events
+            .checkbox
+              = label_tag :abandoned do
+                = check_box_tag :abandoned, 1, params[:abandoned]
+                %span No activity over 6 month
+            .checkbox
+              = label_tag :with_archived do
+                = check_box_tag :with_archived, 1, params[:with_archived]
+                %span Show archived projects
 
-        %fieldset
-          %strong Visibility level:
-          .visibility-levels
-            - Project.visibility_levels.each do |label, level|
-              .checkbox
-                %label
-                  = check_box_tag 'visibility_levels[]', level, params[:visibility_levels].present? && params[:visibility_levels].include?(level.to_s)
-                  %span.descr
-                    = visibility_level_icon(level)
-                    = label
-        %fieldset
-          %strong Problems
-          .checkbox
-            = label_tag :last_repository_check_failed do
-              = check_box_tag :last_repository_check_failed, 1, params[:last_repository_check_failed]
-              %span Last repository check failed
+          %fieldset
+            %strong Visibility level:
+            .visibility-levels
+              - Project.visibility_levels.each do |label, level|
+                .checkbox
+                  %label
+                    = check_box_tag 'visibility_levels[]', level, params[:visibility_levels].present? && params[:visibility_levels].include?(level.to_s)
+                    %span.descr
+                      = visibility_level_icon(level)
+                      = label
+          %fieldset
+            %strong Problems
+            .checkbox
+              = label_tag :last_repository_check_failed do
+                = check_box_tag :last_repository_check_failed, 1, params[:last_repository_check_failed]
+                %span Last repository check failed
 
-        = hidden_field_tag :sort, params[:sort]
-        = button_tag "Search", class: "btn submit btn-primary"
-        = link_to "Reset", admin_namespaces_projects_path, class: "btn btn-cancel"
+          = hidden_field_tag :sort, params[:sort]
+          = button_tag "Search", class: "btn submit btn-primary"
+          = link_to "Reset", admin_namespaces_projects_path, class: "btn btn-cancel"
 
-  %section.col-md-9
-    .panel.panel-default
-      .panel-heading
-        Projects (#{@projects.total_count})
-        .controls
-          .dropdown.inline
-            %button.dropdown-toggle.btn.btn-sm{type: 'button', 'data-toggle' => 'dropdown'}
-              %span.light
-              - if @sort.present?
-                = sort_options_hash[@sort]
-              - else
-                = sort_title_recently_created
-              %b.caret
-            %ul.dropdown-menu
-              %li
-                = link_to admin_namespaces_projects_path(sort: sort_value_recently_created) do
+    %section.col-md-9
+      .panel.panel-default
+        .panel-heading
+          Projects (#{@projects.total_count})
+          .controls
+            .dropdown.inline
+              %button.dropdown-toggle.btn.btn-sm{type: 'button', 'data-toggle' => 'dropdown'}
+                %span.light
+                - if @sort.present?
+                  = sort_options_hash[@sort]
+                - else
                   = sort_title_recently_created
-                = link_to admin_namespaces_projects_path(sort: sort_value_oldest_created) do
-                  = sort_title_oldest_created
-                = link_to admin_namespaces_projects_path(sort: sort_value_recently_updated) do
-                  = sort_title_recently_updated
-                = link_to admin_namespaces_projects_path(sort: sort_value_oldest_updated) do
-                  = sort_title_oldest_updated
-                = link_to admin_namespaces_projects_path(sort: sort_value_largest_repo) do
-                  = sort_title_largest_repo
-          = link_to 'New Project', new_project_path, class: "btn btn-sm btn-success"
-      %ul.well-list
-        - @projects.each do |project|
-          %li
-            .list-item-name
-              %span{ class: visibility_level_color(project.visibility_level) }
-                = visibility_level_icon(project.visibility_level)
-              = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project]
-            .pull-right
-              - if project.archived
-                %span.label.label-warning archived
-              %span.label.label-gray
-                = repository_size(project)
-              = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm"
-              = link_to 'Destroy', [project.namespace.becomes(Namespace), project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-sm btn-remove"
-        - if @projects.blank?
-          .nothing-here-block 0 projects matches
-    = paginate @projects, theme: "gitlab"
+                %b.caret
+              %ul.dropdown-menu
+                %li
+                  = link_to admin_namespaces_projects_path(sort: sort_value_recently_created) do
+                    = sort_title_recently_created
+                  = link_to admin_namespaces_projects_path(sort: sort_value_oldest_created) do
+                    = sort_title_oldest_created
+                  = link_to admin_namespaces_projects_path(sort: sort_value_recently_updated) do
+                    = sort_title_recently_updated
+                  = link_to admin_namespaces_projects_path(sort: sort_value_oldest_updated) do
+                    = sort_title_oldest_updated
+                  = link_to admin_namespaces_projects_path(sort: sort_value_largest_repo) do
+                    = sort_title_largest_repo
+            = link_to 'New Project', new_project_path, class: "btn btn-sm btn-success"
+        %ul.well-list
+          - @projects.each do |project|
+            %li
+              .list-item-name
+                %span{ class: visibility_level_color(project.visibility_level) }
+                  = visibility_level_icon(project.visibility_level)
+                = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project]
+              .pull-right
+                - if project.archived
+                  %span.label.label-warning archived
+                %span.label.label-gray
+                  = repository_size(project)
+                = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm"
+                = link_to 'Destroy', [project.namespace.becomes(Namespace), project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-sm btn-remove"
+          - if @projects.blank?
+            .nothing-here-block 0 projects matches
+      = paginate @projects, theme: "gitlab"
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index 73986d21bcf73c90a1dc435c3d28489fc9167cb0..9e55a562e18032d4cda87d49032792ec72fe6790 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -142,7 +142,7 @@
               %i.fa.fa-pencil-square-o
         %ul.well-list
           - @group_members.each do |member|
-            = render 'groups/group_members/group_member', member: member, show_controls: false
+            = render 'shared/members/member', member: member, show_controls: false
         .panel-footer
           = paginate @group_members, param_name: 'group_members_page', theme: 'gitlab'
 
@@ -172,7 +172,7 @@
                 %span.light Owner
               - else
                 %span.light= project_member.human_access
-                = link_to namespace_project_project_member_path(@project.namespace, @project, project_member), data: { confirm: remove_from_project_team_message(@project, project_member)}, method: :delete, remote: true, class: "btn btn-sm btn-remove" do
+                = link_to namespace_project_project_member_path(@project.namespace, @project, project_member), data: { confirm: remove_member_message(project_member)}, method: :delete, remote: true, class: "btn btn-sm btn-remove" do
                   %i.fa.fa-times
       .panel-footer
         = paginate @project_members, param_name: 'project_members_page', theme: 'gitlab'
diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml
index c3784bf7192df403f0fd1a8091b9d6d95bf0d660..e049b40bfab73ecb01ee1aa3907beafa84c3a32e 100644
--- a/app/views/admin/runners/show.html.haml
+++ b/app/views/admin/runners/show.html.haml
@@ -99,8 +99,8 @@
 
           %td.build-link
             - if project
-              = link_to ci_status_path(build.commit) do
-                %strong #{build.commit.short_sha}
+              = link_to ci_status_path(build.pipeline) do
+                %strong #{build.pipeline.short_sha}
 
           %td.timestamp
             - if build.finished_at
diff --git a/app/views/admin/users/groups.html.haml b/app/views/admin/users/groups.html.haml
index dbecb7bbfd641578894ed8a33f6a1927e0c3e7c2..b0a709a568a62ac73c42b56e888becc704922264 100644
--- a/app/views/admin/users/groups.html.haml
+++ b/app/views/admin/users/groups.html.haml
@@ -13,7 +13,7 @@
           .pull-right
             %span.light= group_member.human_access
             - unless group_member.owner?
-              = link_to group_group_member_path(group, group_member), data: { confirm: remove_user_from_group_message(group, group_member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do
+              = link_to group_group_member_path(group, group_member), data: { confirm: remove_member_message(group_member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do
                 %i.fa.fa-times.fa-inverse
 - else
   .nothing-here-block This user has no groups.
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index 97200f881f785a848895b604828f377022b79568..4365d789829fe1cba149e02fd23c2bc493865a1d 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -1,111 +1,114 @@
+- @no_container = true
 - page_title "Users"
 = render 'shared/show_aside'
+= render "admin/dashboard/head"
 
-.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.filter-external{class: "#{'active' if params[:filter] == 'external'}"}
-      = link_to admin_users_path(filter: 'external') do
-        External
-        %small.badge= number_with_delimiter(User.external.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)
+%div{ class: (container_class) }
+  .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.filter-external{class: "#{'active' if params[:filter] == 'external'}"}
+        = link_to admin_users_path(filter: 'external') do
+          External
+          %small.badge= number_with_delimiter(User.external.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)
 
-  .row-content-block.second-block
-    .pull-right
-      = link_to 'Send email to users', admin_email_path, class: 'btn'
-      .dropdown.inline
-        %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
-          %span.light
-          - 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
+    .row-content-block.second-block
+      .pull-right
+        = link_to 'Send email to users', admin_email_path, class: 'btn'
+        .dropdown.inline
+          %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+            %span.light
+            - if @sort.present?
+              = sort_options_hash[@sort]
+            - else
               = 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
+            %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"
-    = 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
+        = 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?
-            = icon("lock", class: "cred")
-          - else
-            = icon("user", class: "cgreen")
-          = link_to user.name, [:admin, user]
-          - if user.note.present?
-            = link_to "#", { "data-toggle" => "tooltip", title: user.note, class: "user-note"} do
-              = icon("sticky-note-o cgrey")
-          - if user.admin?
-            %strong.cred (Admin)
-          - if user.external?
-            %strong.cred (External)
-          - 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;
+  .panel.panel-default
+    %ul.well-list
+      - @users.each do |user|
+        %li
+          .list-item-name
+            - if user.blocked?
+              = icon("lock", class: "cred")
+            - else
+              = icon("user", class: "cgreen")
+            = link_to user.name, [:admin, user]
+            - if user.note.present?
+              = link_to "#", { "data-toggle" => "tooltip", title: user.note, class: "user-note"} do
+                = icon("sticky-note-o cgrey")
+            - if user.admin?
+              %strong.cred (Admin)
+            - if user.external?
+              %strong.cred (External)
+            - if user == current_user
+              %span.cred It's you!
           .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
-                = 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"
+            %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
+                  = 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/projects.html.haml b/app/views/admin/users/projects.html.haml
index b655b2a15f5b123dcec41050ee77926de734172b..84b9ceb23b3ba11686904ebfb65b460c81e7a911 100644
--- a/app/views/admin/users/projects.html.haml
+++ b/app/views/admin/users/projects.html.haml
@@ -38,6 +38,5 @@
                   %span.light= member.human_access
 
                   - if member.respond_to? :project
-                    = link_to namespace_project_project_member_path(project.namespace, project, member), data: { confirm: remove_from_project_team_message(project, member) }, remote: true, method: :delete, class: "btn-xs btn btn-remove", title: 'Remove user from project' do
+                    = link_to namespace_project_project_member_path(project.namespace, project, member), data: { confirm: remove_member_message(member) }, remote: true, method: :delete, class: "btn-xs btn btn-remove", title: 'Remove user from project' do
                       %i.fa.fa-times
-
diff --git a/app/views/award_emoji/_awards_block.html.haml b/app/views/award_emoji/_awards_block.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..02efcecc8895687433128af2edb40279383bd374
--- /dev/null
+++ b/app/views/award_emoji/_awards_block.html.haml
@@ -0,0 +1,15 @@
+- grouped_emojis = awardable.grouped_awards(with_thumbs: inline)
+.awards.js-awards-block{ class: ("hidden" if !inline && grouped_emojis.empty?), data: { award_url: url_for([:toggle_award_emoji, @project.namespace.becomes(Namespace), @project, awardable]) } }
+  - awards_sort(grouped_emojis).each do |emoji, awards|
+    %button.btn.award-control.js-emoji-btn.has-tooltip{ type: "button", class: (award_active_class(awards, current_user)), data: { placement: "bottom", title: award_user_list(awards, current_user) } }
+      = emoji_icon(emoji, sprite: false)
+      %span.award-control-text.js-counter
+        = awards.count
+
+  - if current_user
+    .award-menu-holder.js-award-holder
+      %button.btn.award-control.js-add-award{ type: "button" }
+        = icon('smile-o', class: "award-control-icon award-control-icon-normal")
+        = icon('spinner spin', class: "award-control-icon award-control-icon-loading")
+        %span.award-control-text
+          Add
diff --git a/app/views/dashboard/_groups_head.html.haml b/app/views/dashboard/_groups_head.html.haml
index 3d17f74b709e53d720367af0c4ee946324ade86b..23c145ebbb42b6e706dab45e851d7e1e2ac5e49f 100644
--- a/app/views/dashboard/_groups_head.html.haml
+++ b/app/views/dashboard/_groups_head.html.haml
@@ -9,5 +9,4 @@
   - if current_user.can_create_group?
     .nav-controls
       = link_to new_group_path, class: "btn btn-new" do
-        = icon('plus')
         New Group
diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml
index 9da3fcbd986c15c2b7a56f033e9c67c424161db1..d35f332e1e01a3f8ba06e73910e90144b02bcecf 100644
--- a/app/views/dashboard/_projects_head.html.haml
+++ b/app/views/dashboard/_projects_head.html.haml
@@ -18,5 +18,4 @@
     = render 'shared/projects/dropdown'
     - if current_user.can_create_project?
       = link_to new_project_path, class: 'btn btn-new' do
-        = icon('plus')
         New Project
diff --git a/app/views/dashboard/issues.atom.builder b/app/views/dashboard/issues.atom.builder
index 0d7b1b30dc3408fd83082146731014d8ff09f63f..0404d0728ea116a9adb8c409657112314058a623 100644
--- a/app/views/dashboard/issues.atom.builder
+++ b/app/views/dashboard/issues.atom.builder
@@ -4,10 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
   xml.link    href: issues_dashboard_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
   xml.link    href: issues_dashboard_url, rel: "alternate", type: "text/html"
   xml.id      issues_dashboard_url
-  xml.updated @issues.first.created_at.xmlschema if @issues.any?
+  xml.updated @issues.first.created_at.xmlschema if @issues.reorder(nil).any?
 
-  @issues.each do |issue|
-    issue_to_atom(xml, issue)
-  end
+  xml << render(partial: 'issues/issue', collection: @issues) if @issues.reorder(nil).any?
 end
-
diff --git a/app/views/dashboard/projects/index.atom.builder b/app/views/dashboard/projects/index.atom.builder
index d4daf07c6c0ee17bf18e191e167d62551eea9c95..fb5be63b47236f2171f8915d8c22bd1de6ecf1c9 100644
--- a/app/views/dashboard/projects/index.atom.builder
+++ b/app/views/dashboard/projects/index.atom.builder
@@ -6,7 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
   xml.id      dashboard_projects_url
   xml.updated @events[0].updated_at.xmlschema if @events[0]
 
-  @events.each do |event|
-    event_to_atom(xml, event)
-  end
+  xml << render(partial: 'events/event', collection: @events) if @events.any?
 end
diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml
index 539f1dc60360615f4949f571ea2f80d6527fba62..98f302d2f937dc769547f9cf9848d0091f41eddb 100644
--- a/app/views/dashboard/todos/_todo.html.haml
+++ b/app/views/dashboard/todos/_todo.html.haml
@@ -3,6 +3,8 @@
     = image_tag avatar_icon(todo.author_email, 40), class: 'avatar s40', alt:''
     .todo-title.title
       - unless todo.build_failed?
+        = todo_target_state_pill(todo)
+
         %span.author-name
           - if todo.author
             = link_to_author(todo)
diff --git a/app/views/devise/confirmations/almost_there.haml b/app/views/devise/confirmations/almost_there.haml
index 3c3830a3f103a233456f5107d0bdb745c4bf04c5..73c3a3dd2eb5f5337b8a95fb363331de7b648e81 100644
--- a/app/views/devise/confirmations/almost_there.haml
+++ b/app/views/devise/confirmations/almost_there.haml
@@ -3,6 +3,9 @@
     Almost there...
   %p.lead
     Please check your email to confirm your account
+- if after_sign_up_text.present?
+  .well-confirmation.text-center
+    = markdown(after_sign_up_text)
 %p.confirmation-content.text-center
   No confirmation email received? Please check your spam folder or
 .append-bottom-20.prepend-top-20.text-center
diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb
deleted file mode 100644
index c6fa8f0ee361b4aa7fe07005943820586a46df7d..0000000000000000000000000000000000000000
--- a/app/views/devise/mailer/confirmation_instructions.html.erb
+++ /dev/null
@@ -1,9 +0,0 @@
-<p>Welcome <%= @resource.name %>!</p>
-
-<% if @resource.unconfirmed_email.present? %>
-  <p>You can confirm your email (<%= @resource.unconfirmed_email %>) through the link below:</p>
-<% else %>
-  <p>You can confirm your account through the link below:</p>
-<% end %>
-
-<p><%= link_to 'Confirm your account', confirmation_url(@resource, confirmation_token: @token) %></p>
diff --git a/app/views/devise/mailer/confirmation_instructions.html.haml b/app/views/devise/mailer/confirmation_instructions.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..086bb8e083d389f231c5abf9d6ef80551a35daf7
--- /dev/null
+++ b/app/views/devise/mailer/confirmation_instructions.html.haml
@@ -0,0 +1,16 @@
+.center
+  - if @resource.unconfirmed_email.present?
+    #content
+      %h2= @resource.unconfirmed_email
+      %p Click the link below to confirm your email address.
+    #cta
+      = link_to 'Confirm your email address', confirmation_url(@resource, confirmation_token: @token)
+  - else
+    #content
+      - if Gitlab.com?
+        %h2 Thanks for signing up to GitLab!
+      - else
+        %h2 Welcome, #{@resource.name}!
+      %p To get started, click the link below to confirm your account.
+    #cta
+      = link_to 'Confirm your account', confirmation_url(@resource, confirmation_token: @token)
diff --git a/app/views/devise/mailer/confirmation_instructions.text.erb b/app/views/devise/mailer/confirmation_instructions.text.erb
new file mode 100644
index 0000000000000000000000000000000000000000..9f76edb76a4ed8a5fe25c199e26ea9e2590d3cd2
--- /dev/null
+++ b/app/views/devise/mailer/confirmation_instructions.text.erb
@@ -0,0 +1,9 @@
+Welcome, <%= @resource.name %>!
+
+<% if @resource.unconfirmed_email.present? %>
+You can confirm your email (<%= @resource.unconfirmed_email %>) through the link below:
+<% else %>
+You can confirm your account through the link below:
+<% end %>
+
+<%= confirmation_url(@resource, confirmation_token: @token) %>
diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml
index c9d1e454a5e174aee6abc9694b67e5e107871d38..a373f61bd3c7bd734eab5c67d63be20ee5c6384d 100644
--- a/app/views/devise/sessions/two_factor.html.haml
+++ b/app/views/devise/sessions/two_factor.html.haml
@@ -1,10 +1,19 @@
 %div
   .login-box
     .login-heading
-      %h3 Two-factor Authentication
+      %h3 Two-Factor Authentication
     .login-body
-      = form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f|
-        = f.text_field :otp_attempt, class: 'form-control', placeholder: 'Two-factor Authentication code', required: true, autofocus: true
-        %p.help-block.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes.
-        .prepend-top-20
-          = f.submit "Verify code", class: "btn btn-save"
+      - if @user.two_factor_otp_enabled?
+        %h5 Authenticate via Two-Factor App
+        = form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f|
+          - resource_params = params[resource_name].presence || params
+          = f.hidden_field :remember_me, value: resource_params.fetch(:remember_me, 0)
+          = f.text_field :otp_attempt, class: 'form-control', placeholder: 'Two-Factor Authentication code', required: true, autofocus: true, autocomplete: 'off'
+          %p.help-block.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes.
+          .prepend-top-20
+            = f.submit "Verify code", class: "btn btn-save"
+
+      - if @user.two_factor_u2f_enabled?
+
+        %hr
+        = render "u2f/authenticate"
diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml
index 510215bb8cdbc8301720b91eead4f689ba50753b..905a8dbcd841ac80d82f56fe42916da0f53d7d85 100644
--- a/app/views/devise/shared/_signup_box.html.haml
+++ b/app/views/devise/shared/_signup_box.html.haml
@@ -16,7 +16,7 @@
       %div
         = f.email_field :email, class: "form-control middle", placeholder: "Email", required: true
       .form-group.append-bottom-20#password-strength
-        = f.password_field :password, class: "form-control bottom", placeholder: "Password", required: true
+        = f.password_field :password, class: "form-control bottom", placeholder: "Password - minimum length #{@minimum_password_length} characters", required: true, pattern: ".{#{@minimum_password_length},}", title: "Minimum length is #{@minimum_password_length} characters"
       %div
       - if current_application_settings.recaptcha_enabled
         = recaptcha_tags
diff --git a/app/views/doorkeeper/authorizations/new.html.haml b/app/views/doorkeeper/authorizations/new.html.haml
index eae80e5210fd2f29e2ef58b09ea558e076f99044..ce050007204f141232e0b81dcd27657e551eaea6 100644
--- a/app/views/doorkeeper/authorizations/new.html.haml
+++ b/app/views/doorkeeper/authorizations/new.html.haml
@@ -1,4 +1,4 @@
-%h3.page-title Authorize required
+%h3.page-title Authorization required
 %main{:role => "main"}
   %p.h4
     Authorize
diff --git a/app/views/emojis/index.html.haml b/app/views/emojis/index.html.haml
index 3443a8e2307f5e3e611a1852c2e9e4d003589372..97401a2e618eca912784df91c5baaaf28ae8a784 100644
--- a/app/views/emojis/index.html.haml
+++ b/app/views/emojis/index.html.haml
@@ -1,9 +1,9 @@
 .emoji-menu
   .emoji-menu-content
     = text_field_tag :emoji_search, "", class: "emoji-search search-input form-control"
-    - AwardEmoji.emoji_by_category.each do |category, emojis|
+    - Gitlab::AwardEmoji.emoji_by_category.each do |category, emojis|
       %h5.emoji-menu-title
-        = AwardEmoji::CATEGORIES[category]
+        = Gitlab::AwardEmoji::CATEGORIES[category]
       %ul.clearfix.emoji-menu-list
         - emojis.each do |emoji|
           %li.pull-left.text-center.emoji-menu-list-item
diff --git a/app/views/events/_commit.html.haml b/app/views/events/_commit.html.haml
index dce4081288cecbea3a604d8dfd09f8e4b84d9986..1bc9f6044385883337e6f404d77b3f16acab33c9 100644
--- a/app/views/events/_commit.html.haml
+++ b/app/views/events/_commit.html.haml
@@ -2,4 +2,4 @@
   .commit-row-title
     = link_to truncate_sha(commit[:id]), namespace_project_commit_path(project.namespace, project, commit[:id]), class: "commit_short_id", alt: '', title: truncate_sha(commit[:id])
     &middot;
-    = markdown event_commit_title(commit[:message]), project: project, pipeline: :single_line
+    = markdown event_commit_title(commit[:message]), project: project, pipeline: :single_line, author: event.author
diff --git a/app/views/events/_event.atom.builder b/app/views/events/_event.atom.builder
new file mode 100644
index 0000000000000000000000000000000000000000..7890e717aa7c60e74b3a4bc40fb4c95e73427f3d
--- /dev/null
+++ b/app/views/events/_event.atom.builder
@@ -0,0 +1,20 @@
+return unless event.visible_to_user?(current_user)
+
+xml.entry do
+  xml.id      "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"
+  xml.link    href: event_feed_url(event)
+  xml.title   truncate(event_feed_title(event), length: 80)
+  xml.updated event.created_at.xmlschema
+  xml.media   :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(event.author_email))
+
+  xml.author do
+    xml.name event.author_name
+    xml.email event.author_email
+  end
+
+  xml.summary(type: "xhtml") do |summary|
+    event_summary = event_feed_summary(event)
+
+    summary << event_summary unless event_summary.nil?
+  end
+end
diff --git a/app/views/events/_event_issue.atom.haml b/app/views/events/_event_issue.atom.haml
index fad65310021542a8603f16bed8f320960fa2b2af..083c3936212d091f247cb37f7e49ef7fe4e239dd 100644
--- a/app/views/events/_event_issue.atom.haml
+++ b/app/views/events/_event_issue.atom.haml
@@ -1,2 +1,2 @@
 %div{xmlns: "http://www.w3.org/1999/xhtml"}
-  = markdown(issue.description, pipeline: :atom, project: issue.project)
+  = markdown(issue.description, pipeline: :atom, project: issue.project, author: issue.author)
diff --git a/app/views/events/_event_merge_request.atom.haml b/app/views/events/_event_merge_request.atom.haml
index 19bdc7b9ca540f9de4607f90e64901f55aac4411..d7e05600627cc65fa7264c52d47b0fededa15a9a 100644
--- a/app/views/events/_event_merge_request.atom.haml
+++ b/app/views/events/_event_merge_request.atom.haml
@@ -1,2 +1,2 @@
 %div{xmlns: "http://www.w3.org/1999/xhtml"}
-  = markdown(merge_request.description, pipeline: :atom, project: merge_request.project)
+  = markdown(merge_request.description, pipeline: :atom, project: merge_request.project, author: merge_request.author)
diff --git a/app/views/events/_event_note.atom.haml b/app/views/events/_event_note.atom.haml
index b730ebbd5f9f5052b272d89939b0e2f6540580d4..1154f98282130b7d506c130e370d83d027b7ca29 100644
--- a/app/views/events/_event_note.atom.haml
+++ b/app/views/events/_event_note.atom.haml
@@ -1,2 +1,2 @@
 %div{xmlns: "http://www.w3.org/1999/xhtml"}
-  = markdown(note.note, pipeline: :atom, project: note.project)
+  = markdown(note.note, pipeline: :atom, project: note.project, author: note.author)
diff --git a/app/views/events/_event_push.atom.haml b/app/views/events/_event_push.atom.haml
index b271b9daff1160fba791da0c669b81188ade9f8a..28bee1d0a33a53a9adf69c801e0b90b4b68d2a29 100644
--- a/app/views/events/_event_push.atom.haml
+++ b/app/views/events/_event_push.atom.haml
@@ -6,7 +6,7 @@
       %i
         at
         = commit[:timestamp].to_time.to_s(:short)
-    %blockquote= markdown(escape_once(commit[:message]), pipeline: :atom, project: event.project)
+    %blockquote= markdown(escape_once(commit[:message]), pipeline: :atom, project: event.project, author: event.author)
   - if event.commits_count > 15
     %p
       %i
diff --git a/app/views/events/event/_common.html.haml b/app/views/events/event/_common.html.haml
index f9f623cc031a7c092b26f3efbfcebe7d55f38b71..2e2403347c1b5b37451871faab4c8733b0f4f667 100644
--- a/app/views/events/event/_common.html.haml
+++ b/app/views/events/event/_common.html.haml
@@ -1,10 +1,14 @@
 .event-title
   %span.author_name= link_to_author event
   %span.event_label{class: event.action_name}
-    = event_action_name(event)
-
   - if event.target
-    %strong= link_to event.target.reference_link_text, [event.project.namespace.becomes(Namespace), event.project, event.target], title: event.target_title
+    = event.action_name
+    %strong
+      = link_to [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip', title: event.target_title do
+        = event.target_type.titleize.downcase
+        = event.target.reference_link_text
+  - else
+    = event_action_name(event)
 
   = event_preposition(event)
 
diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml
index 235bd46107ee7dcdedcb8aaaa0cb7a405bcda854..dc4ff17e31abfc4bfd704e28327f8945cc6496a4 100644
--- a/app/views/events/event/_push.html.haml
+++ b/app/views/events/event/_push.html.haml
@@ -15,7 +15,7 @@
     %ul.well-list.event_commits
       - few_commits = event.commits[0...2]
       - few_commits.each do |commit|
-        = render "events/commit", commit: commit, project: project
+        = render "events/commit", commit: commit, project: project, event: event
 
       - create_mr = event.new_ref? && create_mr_button?(event.project.default_branch, event.ref_name, event.project)
       - if event.commits_count > 1
diff --git a/app/views/groups/group_members/_group_member.html.haml b/app/views/groups/group_members/_group_member.html.haml
deleted file mode 100644
index 60234be8f83f0d478130720f8ba2268a7f70bf93..0000000000000000000000000000000000000000
--- a/app/views/groups/group_members/_group_member.html.haml
+++ /dev/null
@@ -1,57 +0,0 @@
-- user = member.user
-- return unless user || member.invite?
-- show_roles = local_assigns.fetch(:show_roles, true)
-
-%li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)}
-  %span{class: ("list-item-name" if show_controls)}
-    - if member.user
-      = image_tag avatar_icon(user, 24), class: "avatar s24", alt: ''
-      %strong
-        = link_to user.name, user_path(user)
-      %span.cgray= user.username
-      - if user == current_user
-        %span.label.label-success It's you
-      - if user.blocked?
-        %label.label.label-danger
-          %strong Blocked
-    - else
-      = image_tag avatar_icon(member.invite_email, 24), class: "avatar s24", alt: ''
-      %strong
-        = member.invite_email
-      %span.cgray
-        invited
-        - if member.created_by
-          by
-          = link_to member.created_by.name, user_path(member.created_by)
-        = time_ago_with_tooltip(member.created_at)
-
-      - if show_controls && can?(current_user, :admin_group_member, @group)
-        = link_to resend_invite_group_group_member_path(@group, member), method: :post, class: "btn-xs btn", title: 'Resend invite' do
-          Resend invite
-
-  - if show_roles && should_user_see_group_roles?(current_user, @group)
-    %span.pull-right
-      %strong.member-access-level= member.human_access
-      - if show_controls
-        - if can?(current_user, :update_group_member, member)
-          = button_tag class: "btn-xs btn js-toggle-button",
-                       title: 'Edit access level', type: 'button' do
-            %i.fa.fa-pencil-square-o
-
-        - if can?(current_user, :destroy_group_member, member)
-          &nbsp;
-          - if current_user == user
-            = link_to leave_group_group_members_path(@group), data: { confirm: leave_group_message(@group.name)}, method: :delete, class: "btn-xs btn btn-remove", title: 'Remove user from group' do
-              = icon("sign-out")
-              Leave
-          - else
-            = link_to group_group_member_path(@group, member), data: { confirm: remove_user_from_group_message(@group, member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do
-              %i.fa.fa-minus.fa-inverse
-
-    .edit-member.hide.js-toggle-content
-      %br
-      = form_for [@group, member], remote: true do |f|
-        .prepend-top-10
-          = f.select :access_level, options_for_select(GroupMember.access_level_roles, member.access_level), {}, class: 'form-control'
-        .prepend-top-10
-          = f.submit 'Save', class: 'btn btn-save btn-sm'
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index b18459285811008a84c7e6a566ba65a62db81c64..a506cb9ff8b89c5e9e41c0a140059f6b851a47a0 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -6,9 +6,8 @@
       .panel-heading
         Add new user to group
       .panel-body
-        - if should_user_see_group_roles?(current_user, @group)
-          %p.light
-            Members of group have access to all group projects.
+        %p.light
+          Members of group have access to all group projects.
         .new-group-member-holder
           = render "new_group_member"
 
@@ -30,7 +29,10 @@
             data: { "confirm-danger-message" => clear_ldap_permission_cache_message,
                     'warning-message' => 'If you made manual permission tweaks for some group members they will be lost.' }
 
-  
+
+  - if current_user && can?(current_user, :admin_group_member, @group)
+    = render 'shared/members/requests', membership_source: @group, members: @members.request
+
   .panel.panel-default
     .panel-heading
       %strong #{@group.name}
@@ -44,9 +46,8 @@
           = button_tag class: 'btn', title: 'Search' do
             = icon("search")
     %ul.content-list
-      - @members.each do |member|
-        = render 'groups/group_members/group_member', member: member, show_controls: true
-    = paginate @members, theme: 'gitlab'
+      = render partial: 'shared/members/member', collection: @members.non_request, as: :member
+    = paginate @members.non_request, theme: 'gitlab'
 
 :javascript
   $('form.member-search-form').on('submit', function(event) {
diff --git a/app/views/groups/group_members/update.js.haml b/app/views/groups/group_members/update.js.haml
index df726e2b2b9f355528af9b3ba3247c159815847c..b0b3a51ce58629cbf6d25df0d48b6e567424811a 100644
--- a/app/views/groups/group_members/update.js.haml
+++ b/app/views/groups/group_members/update.js.haml
@@ -1,2 +1,2 @@
 :plain
-  $("##{dom_id(@group_member)}").replaceWith('#{escape_javascript(render(@group_member, member: @group_member, show_controls: true))}');
+  $("##{dom_id(@group_member)}").replaceWith('#{escape_javascript(render(@group_member, member: @group_member))}');
diff --git a/app/views/groups/hooks/_project_hook.html.haml b/app/views/groups/hooks/_project_hook.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..599e66b9ccd61a4827d2c4198be468d5106ab4bb
--- /dev/null
+++ b/app/views/groups/hooks/_project_hook.html.haml
@@ -0,0 +1,15 @@
+%li
+  .row
+    .col-md-8.col-lg-7
+      %strong.light-header= hook.url
+      %div
+        - %w(push_events tag_push_events issues_events note_events merge_requests_events build_events wiki_page_events).each do |trigger|
+          - if hook.send(trigger)
+            %span.label.label-gray.deploy-project-label= trigger.titleize
+    .col-md-4.col-lg-5.text-right-lg.prepend-top-5
+      %span.append-right-10.inline
+        SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
+      = link_to "Test", test_group_hook_path(@group, hook), class: "btn btn-sm"
+      = link_to group_hook_path(@group, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-transparent" do
+        %span.sr-only Remove
+        = icon('trash')
diff --git a/app/views/groups/issues.atom.builder b/app/views/groups/issues.atom.builder
index a6eb9abada6676f8bc8442b2cfe703f455c2df54..b16280403253f890204ff6b74dba591caacc3c70 100644
--- a/app/views/groups/issues.atom.builder
+++ b/app/views/groups/issues.atom.builder
@@ -4,10 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
   xml.link    href: issues_group_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
   xml.link    href: issues_group_url, rel: "alternate", type: "text/html"
   xml.id      issues_group_url
-  xml.updated @issues.first.created_at.xmlschema if @issues.any?
+  xml.updated @issues.first.created_at.xmlschema if @issues.reorder(nil).any?
 
-  @issues.each do |issue|
-    issue_to_atom(xml, issue)
-  end
+  xml << render(partial: 'issues/issue', collection: @issues) if @issues.reorder(nil).any?
 end
-
diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml
index 7d9d27ae1fccc98f044e33e77ebf95d5c26ce6e8..ca6c4326d1c38a3c19ba3b68257fa41bd1e6c5cf 100644
--- a/app/views/groups/milestones/new.html.haml
+++ b/app/views/groups/milestones/new.html.haml
@@ -39,9 +39,8 @@
     .col-md-6
       .form-group
         = f.label :due_date, "Due Date", class: "control-label"
-        .col-sm-10= f.hidden_field :due_date
         .col-sm-10
-          .datepicker
+          = f.text_field :due_date, class: "datepicker form-control", placeholder: "Select due date"
 
   .form-actions
     = f.submit 'Create Milestone', class: "btn-create btn"
diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder
index c66b82bb484f06b4099d7b51c3ae19b580e2bb92..b68bf444d27f5e0385dac03ba607d15ff1cd60e2 100644
--- a/app/views/groups/show.atom.builder
+++ b/app/views/groups/show.atom.builder
@@ -6,7 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
   xml.id      group_url(@group)
   xml.updated @events[0].updated_at.xmlschema if @events[0]
 
-  @events.each do |event|
-    event_to_atom(xml, event)
-  end
+  xml << render(@events) if @events.any?
 end
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 77c297255b8367ba727e52307a43a877feb2c33f..62ebd69485cd80d8da617ed2da982b29bc5a3aed 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -5,7 +5,7 @@
     = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity")
 
 .cover-block.groups-cover-block
-  .container-fluid.container-limited
+  %div{ class: (container_class) }
     = link_to group_icon(@group), target: '_blank' do
       = image_tag group_icon(@group), class: "avatar group-avatar s70"
     .group-info
@@ -19,6 +19,9 @@
         .cover-desc.description
           = markdown(@group.description, pipeline: :description)
 
+    - if current_user
+      = render 'shared/members/access_request_buttons', source: @group
+
 %div{ class: container_class }
   .top-area
     %ul.nav-links
@@ -35,7 +38,6 @@
       = render 'shared/projects/dropdown'
       - if can? current_user, :create_projects, @group
         = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new pull-right' do
-          = icon('plus')
           New Project
 
   .tab-content
diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml
index 70e88da7aaeb7e0c4e8fbc4f3cd721ad1ecfb3d6..01648047ce20ecfb9ffba7490196da0e1b35f3ac 100644
--- a/app/views/help/_shortcuts.html.haml
+++ b/app/views/help/_shortcuts.html.haml
@@ -24,7 +24,7 @@
                 %td Show/hide this dialog
               %tr
                 %td.shortcut
-                  - if browser.mac?
+                  - if browser.platform.mac?
                     .key &#8984; shift p
                   - else
                     .key ctrl shift p
diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml
index 5b7f11440c102e7b3e0061f7812454b33a59c4b9..6c4a9d68d1f4aaf0cbe722dbf5b8498616cf4801 100644
--- a/app/views/import/github/status.html.haml
+++ b/app/views/import/github/status.html.haml
@@ -4,6 +4,10 @@
   %i.fa.fa-github
   Import projects from GitHub
 
+%p
+  %i.fa.fa-warning
+  To import GitHub pull requests, any pull request source branches that had been deleted are temporarily restored on GitHub. To prevent any connected CI services from being overloaded with dozens of irrelevant branches being created and deleted again, GitHub webhooks are temporarily disabled during the import process.
+
 %p.light
   Select projects you want to import.
 %hr
diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml
index e3a356b53792c26fd88c805159462044ab5d682a..aedb8468eca9e1d331906cf4955fbbb2bf5f14ea 100644
--- a/app/views/import/gitlab/status.html.haml
+++ b/app/views/import/gitlab/status.html.haml
@@ -47,7 +47,7 @@
           %td.import-target
             = repo["path_with_namespace"]
           %td.import-actions.job-status
-            = button_tag class: "btn js-add-to-import" do
+            = button_tag class: "btn btn-import js-add-to-import" do
               Import
               = icon("spinner spin", class: "loading-icon")
 
diff --git a/app/views/issues/_issue.atom.builder b/app/views/issues/_issue.atom.builder
new file mode 100644
index 0000000000000000000000000000000000000000..968318741447739462ddfd5834e35e6582c6c5ef
--- /dev/null
+++ b/app/views/issues/_issue.atom.builder
@@ -0,0 +1,32 @@
+xml.entry do
+  xml.id      namespace_project_issue_url(issue.project.namespace, issue.project, issue)
+  xml.link    href: namespace_project_issue_url(issue.project.namespace, issue.project, issue)
+  xml.title   truncate(issue.title, length: 80)
+  xml.updated issue.created_at.xmlschema
+  xml.media   :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(issue.author_email))
+
+  xml.author do
+    xml.name issue.author_name
+    xml.email issue.author_email
+  end
+
+  xml.summary issue.title
+  xml.description issue.description if issue.description
+  xml.milestone issue.milestone.title if issue.milestone
+  xml.due_date issue.due_date if issue.due_date
+
+  unless issue.labels.empty?
+    xml.labels do
+      issue.labels.each do |label|
+        xml.label label.name
+      end
+    end
+  end
+
+  if issue.assignee
+    xml.assignee do
+      xml.name issue.assignee.name
+      xml.email issue.assignee.email
+    end
+  end
+end
diff --git a/app/views/kaminari/gitlab/_first_page.html.haml b/app/views/kaminari/gitlab/_first_page.html.haml
index ada7306d98de107f31cd026ba84998ef71a64ced..e7a70e3bb28d5be60353ebac0b12e63805519d0c 100644
--- a/app/views/kaminari/gitlab/_first_page.html.haml
+++ b/app/views/kaminari/gitlab/_first_page.html.haml
@@ -2,7 +2,7 @@
 -#  available local variables
 -#    url:           url to the first page
 -#    current_page:  a page object for the currently displayed page
--#    num_pages:     total number of pages
+-#    total_pages:   total number of pages
 -#    per_page:      number of items to fetch per page
 -#    remote:        data-remote
 %li.first
diff --git a/app/views/kaminari/gitlab/_gap.html.haml b/app/views/kaminari/gitlab/_gap.html.haml
index 3ffd12f8587aa72b3929dbdcbfbaf999383111b6..80ca30f36e66f10675a6067016fd82e31ed7f3fc 100644
--- a/app/views/kaminari/gitlab/_gap.html.haml
+++ b/app/views/kaminari/gitlab/_gap.html.haml
@@ -1,7 +1,7 @@
 -#  Non-link tag that stands for skipped pages...
 -#  available local variables
 -#    current_page:  a page object for the currently displayed page
--#    num_pages:     total number of pages
+-#    total_pages:   total number of pages
 -#    per_page:      number of items to fetch per page
 -#    remote:        data-remote
 %li{class: "page"}
diff --git a/app/views/kaminari/gitlab/_last_page.html.haml b/app/views/kaminari/gitlab/_last_page.html.haml
index 3431d029bcce5731753ac08dee18d1c545e9c845..53f780d1d1bc964d41f3d523bb148f6bd73c0c79 100644
--- a/app/views/kaminari/gitlab/_last_page.html.haml
+++ b/app/views/kaminari/gitlab/_last_page.html.haml
@@ -2,7 +2,7 @@
 -#  available local variables
 -#    url:           url to the last page
 -#    current_page:  a page object for the currently displayed page
--#    num_pages:     total number of pages
+-#    total_pages:   total number of pages
 -#    per_page:      number of items to fetch per page
 -#    remote:        data-remote
 %li.last
diff --git a/app/views/kaminari/gitlab/_next_page.html.haml b/app/views/kaminari/gitlab/_next_page.html.haml
index c805914fc3fcc03033c27734eefa49c2173b711f..125f09777ba50366f6e0499f774e39a584cda27d 100644
--- a/app/views/kaminari/gitlab/_next_page.html.haml
+++ b/app/views/kaminari/gitlab/_next_page.html.haml
@@ -2,7 +2,7 @@
 -#  available local variables
 -#    url:           url to the next page
 -#    current_page:  a page object for the currently displayed page
--#    num_pages:     total number of pages
+-#    total_pages:   total number of pages
 -#    per_page:      number of items to fetch per page
 -#    remote:        data-remote
 - if current_page.last?
diff --git a/app/views/kaminari/gitlab/_page.html.haml b/app/views/kaminari/gitlab/_page.html.haml
index a52d883b9a84611e1f272aaf001b04bfdefdda2d..522e4d1d05fd57c70fb789f8d9177df38b055e5a 100644
--- a/app/views/kaminari/gitlab/_page.html.haml
+++ b/app/views/kaminari/gitlab/_page.html.haml
@@ -3,7 +3,7 @@
 -#    page:          a page object for "this" page
 -#    url:           url to this page
 -#    current_page:  a page object for the currently displayed page
--#    num_pages:     total number of pages
+-#    total_pages:   total number of pages
 -#    per_page:      number of items to fetch per page
 -#    remote:        data-remote
 %li{class: "page#{' active' if page.current?}"}
diff --git a/app/views/kaminari/gitlab/_paginator.html.haml b/app/views/kaminari/gitlab/_paginator.html.haml
index a12c53bcfe713401ebcf6d1e026f5edd59cf6917..f5e0d2ed3f30dfd54914eafbf51ee7ecc1b6dafc 100644
--- a/app/views/kaminari/gitlab/_paginator.html.haml
+++ b/app/views/kaminari/gitlab/_paginator.html.haml
@@ -1,7 +1,7 @@
 -#  The container tag
 -#  available local variables
 -#    current_page:  a page object for the currently displayed page
--#    num_pages:     total number of pages
+-#    total_pages:   total number of pages
 -#    per_page:      number of items to fetch per page
 -#    remote:        data-remote
 -#    paginator:     the paginator that renders the pagination tags inside
@@ -9,7 +9,7 @@
   %div.gl-pagination
     %ul.pagination.clearfix
       - unless current_page.first?
-        = first_page_tag unless num_pages < 5 # As kaminari will always show the first 5 pages
+        = first_page_tag unless total_pages < 5 # As kaminari will always show the first 5 pages
       = prev_page_tag
       - each_page do |page|
         - if page.left_outer? || page.right_outer? || page.inside_window?
@@ -18,5 +18,5 @@
           = gap_tag
       = next_page_tag
       - unless current_page.last?
-        = last_page_tag unless num_pages < 5
+        = last_page_tag unless total_pages < 5
 
diff --git a/app/views/kaminari/gitlab/_prev_page.html.haml b/app/views/kaminari/gitlab/_prev_page.html.haml
index afb20455e0a3727f2fb70f7fed0b489a9af03f43..7edf10498a80e230d03240f393351f538a3b08cb 100644
--- a/app/views/kaminari/gitlab/_prev_page.html.haml
+++ b/app/views/kaminari/gitlab/_prev_page.html.haml
@@ -2,7 +2,7 @@
 -#  available local variables
 -#    url:           url to the previous page
 -#    current_page:  a page object for the currently displayed page
--#    num_pages:     total number of pages
+-#    total_pages:   total number of pages
 -#    per_page:      number of items to fetch per page
 -#    remote:        data-remote
 - if current_page.first?
diff --git a/app/views/layouts/_collapse_button.html.haml b/app/views/layouts/_collapse_button.html.haml
index 2ed51d87ca1332d8c9d7b34ba3a7219b662a53f9..8c140a5943eb9db8ee56d8dcca8aaea9f96c6fbc 100644
--- a/app/views/layouts/_collapse_button.html.haml
+++ b/app/views/layouts/_collapse_button.html.haml
@@ -1,4 +1,3 @@
-- if nav_menu_collapsed?
-  = link_to icon('angle-right'), '#', class: 'toggle-nav-collapse', title: "Open/Close"
-- else
-  = link_to icon('angle-left'), '#', class: 'toggle-nav-collapse', title: "Open/Close"
+= link_to '#', class: 'nav-header-btn text-center toggle-nav-collapse', title: "Open/Close" do
+  %span.sr-only Toggle navigation
+  = icon('bars')
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 79cdbac1f37bae1914fbedd157df8290c7dae48a..e0ed657919eb2cca180e3b80e52e97ffc82e276a 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -30,9 +30,10 @@
 
   = javascript_include_tag "application"
 
-  = csrf_meta_tags
+  - if page_specific_javascripts
+    = javascript_include_tag page_specific_javascripts, {"data-turbolinks-track" => true}
 
-  = include_gon
+  = csrf_meta_tags
 
   - unless browser.safari?
     %meta{name: 'referrer', content: 'origin-when-cross-origin'}
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 5be0b546a626a5564fc36f35235bc22a23b8187c..199ab3c38c3eecb81822554046ada54e667d40a8 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -1,12 +1,6 @@
 .page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
   .sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
-    .header-logo
-      %a#logo
-        = brand_header_logo
-      = link_to root_path, class: 'gitlab-text-container-link', title: 'Dashboard', id: 'js-shortcuts-home' do
-        .gitlab-text-container
-          %h3 GitLab
-
+    = render partial: 'layouts/collapse_button'
     - if defined?(sidebar) && sidebar
       = render "layouts/nav/#{sidebar}"
     - elsif current_user
@@ -14,18 +8,19 @@
     - else
       = render 'layouts/nav/explore'
 
-    .collapse-nav
-      = render partial: 'layouts/collapse_button'
     - if current_user
-      = link_to current_user, class: 'sidebar-user', title: "Profile" do
+      = link_to current_user, class: 'sidebar-user', title: "Profile", data: {user: current_user.username} do
         = image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36'
         .username
           = current_user.username
+    = link_to '#', class: "nav-header-btn text-center pin-nav-btn #{'is-active' if pinned_nav?} js-nav-pin", title: 'Pin/Unpin navigation' do
+      %span.sr-only Toggle navigation pinning
+      = icon('thumb-tack')
   - if defined?(nav) && nav
     .layout-nav
       .container-fluid
         = render "layouts/nav/#{nav}"
-  .content-wrapper{ class: "#{layout_nav_class} #{layout_dropdown_class}" }
+  .content-wrapper{ class: "#{layout_nav_class}" }
     = render "layouts/broadcast"
     = render "layouts/flash"
     = yield :flash_message
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index 6b208c3d0bb4858ca0c23a108f3d13d8e4e894de..245b9c3b4d447a735cb3074f7a44cb9817787c5f 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -6,11 +6,8 @@
 .search.search-form{class: "#{'has-location-badge' if label.present?}"}
   = form_tag search_path, method: :get, class: 'navbar-form' do |f|
     .search-input-container
-      .search-location-badge
-        - if label.present?
-          %span.location-badge
-            %i.location-text
-              = label
+      - if label.present?
+        .location-badge= label
       .search-input-wrap
         .dropdown{ data: {url: search_autocomplete_path } }
           = search_field_tag "search", nil, placeholder: 'Search', class: "search-input dropdown-menu-toggle", spellcheck: false, tabindex: "1", autocomplete: 'off',  data: { toggle: 'dropdown' }
@@ -39,6 +36,31 @@
       - else
         = hidden_field_tag :search_code, true
 
+      :javascript
+        gl.projectOptions = gl.projectOptions || {};
+        gl.projectOptions["#{j(@project.path)}"] = {
+          issuesPath: "#{namespace_project_issues_path(@project.namespace, @project)}",
+          mrPath: "#{namespace_project_merge_requests_path(@project.namespace, @project)}",
+          name: "#{j(@project.name)}"
+        };
+
+    - if @group and @group.path
+      :javascript
+        gl.groupOptions = gl.groupOptions || {};
+        gl.groupOptions["#{j(@group.path)}"] = {
+          name: "#{j(@group.name)}",
+          issuesPath: "#{issues_group_path(j(@group.path))}",
+          mrPath: "#{merge_requests_group_path(j(@group.path))}"
+        };
+
+
+    :javascript
+      gl.dashboardOptions = {
+        issuesPath: "#{issues_dashboard_url}",
+        mrPath: "#{merge_requests_dashboard_url}"
+      };
+
+
     - if @snippet || @snippets
       = hidden_field_tag :snippets, true
     = hidden_field_tag :repository_ref, @ref
diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml
index 6591c52bdbde6c3e9bc01f02ad5fee5374f3361a..87064cc9b3f2261117732543da2928e8a21bdad1 100644
--- a/app/views/layouts/admin.html.haml
+++ b/app/views/layouts/admin.html.haml
@@ -1,5 +1,5 @@
 - page_title    "Admin Area"
 - header_title  "Admin Area", admin_root_path
-- sidebar       "admin"
+- nav           "admin"
 
 = render template: "layouts/application"
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index e4d1c773d0390ea00cb84b65ff470a7e070057be..33cedaaf2eee64e17ae15780b35309b275cb9f83 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -1,7 +1,9 @@
 !!! 5
 %html{ lang: "en"}
   = render "layouts/head"
-  %body{class: "#{user_application_theme}", 'data-page' => body_data_page}
+  %body{class: "#{user_application_theme}", data: {page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}"}}
+    = Gon::Base.render_data
+
     -# Ideally this would be inside the head, but turbolinks only evaluates page-specific JS in the body.
     = yield :scripts_body_top
 
diff --git a/app/views/layouts/ci/_page.html.haml b/app/views/layouts/ci/_page.html.haml
index a13241bebee9bd7947a8d8859c7ec9faebb303b3..2e56d0ac6a33fdfa1cfa8dc24a76920c36c54e82 100644
--- a/app/views/layouts/ci/_page.html.haml
+++ b/app/views/layouts/ci/_page.html.haml
@@ -1,12 +1,6 @@
 .page-with-sidebar{ class: page_sidebar_class }
   = render "layouts/broadcast"
   .sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
-    .header-logo
-      %a#logo
-        = brand_header_logo
-      = link_to root_path, class: 'gitlab-text-container-link', title: 'Dashboard', id: 'js-shortcuts-home' do
-        .gitlab-text-container
-          %h3 GitLab
 
     - if defined?(sidebar) && sidebar
       = render "layouts/ci/#{sidebar}"
diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml
index 0a2df4ad0144814dd5c1ba6818da32145faaab10..86de3b7233770a783716565146aee5a388a89235 100644
--- a/app/views/layouts/devise.html.haml
+++ b/app/views/layouts/devise.html.haml
@@ -2,6 +2,7 @@
 %html{ lang: "en"}
   = render "layouts/head"
   %body.ui_charcoal.login-page.application.navless
+    = Gon::Base.render_data
     = render "layouts/header/empty"
     = render "layouts/broadcast"
     .container.navless-container
diff --git a/app/views/layouts/devise_empty.html.haml b/app/views/layouts/devise_empty.html.haml
index 7c061dd531fd0cf4ea4125b08843cfae40d25027..6bd427b02ac919407a03896dd2924a034444c7be 100644
--- a/app/views/layouts/devise_empty.html.haml
+++ b/app/views/layouts/devise_empty.html.haml
@@ -2,6 +2,7 @@
 %html{ lang: "en"}
   = render "layouts/head"
   %body.ui_charcoal.login-page.application.navless
+    = Gon::Base.render_data
     = render "layouts/header/empty"
     = render "layouts/broadcast"
     .container.navless-container
diff --git a/app/views/layouts/devise_mailer.html.haml b/app/views/layouts/devise_mailer.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..c258eafdd51c2234939c69fd7a6ce699d727e074
--- /dev/null
+++ b/app/views/layouts/devise_mailer.html.haml
@@ -0,0 +1,34 @@
+!!! 5
+%html
+  %head
+    %meta(content='text/html; charset=UTF-8' http-equiv='Content-Type')
+    = stylesheet_link_tag 'mailers/devise'
+
+  %body
+    %table#wrapper
+      %tr
+        %td
+          %table#header
+            %td{valign: "top"}
+              = image_tag('mailers/gitlab_header_logo.png', id: 'logo', alt: 'GitLab Wordmark')
+
+          %table#body
+            %tr
+              %td#body-container
+                = yield
+
+          - if Gitlab.com?
+            %table#footer
+              %tr
+                %td#tanuki
+                  = image_tag('mailers/gitlab_tanuki_2x.png', alt: 'GitLab Logo')
+              %tr
+                %td#tagline
+                  Everyone can contribute
+              %tr
+                %td#social
+                  = link_to 'Blog',     'https://about.gitlab.com/blog/'
+                  = link_to 'Twitter',  'https://twitter.com/gitlab'
+                  = link_to 'Facebook', 'https://www.facebook.com/gitlab/'
+                  = link_to 'YouTube',  'https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg'
+                  = link_to 'LinkedIn', 'https://www.linkedin.com/company/gitlab-com'
diff --git a/app/views/layouts/errors.html.haml b/app/views/layouts/errors.html.haml
index 915acc4612e1a51a5f0cc883a8fce869b21ce293..7fbe065df00cf0900501571bc9b3fa5fde4016d2 100644
--- a/app/views/layouts/errors.html.haml
+++ b/app/views/layouts/errors.html.haml
@@ -2,6 +2,7 @@
 %html{ lang: "en"}
   = render "layouts/head"
   %body{class: "#{user_application_theme} application navless"}
+    = Gon::Base.render_data
     = render "layouts/header/empty"
     .container.navless-container
       = render "layouts/flash"
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index c33740e23fad45ad41aac4d59281c1d1f7e687b9..b007b3d8477ff5e47cc54dac563a00a00fe210ac 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -27,9 +27,12 @@
             %li
               = link_to dashboard_todos_path, title: 'Todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
                 = icon('bell fw')
-                - unless todos_pending_count == 0
-                  %span.badge.todos-pending-count
-                    = todos_pending_count
+                %span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0) }
+                  = todos_pending_count
+            - if Gitlab::Geo.secondary?
+              %li
+                = link_to Gitlab::Geo.primary_node.url, title: 'Go to primary node', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+                  = icon('globe fw')
             - if current_user.can_create_project?
               %li
                 = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
@@ -50,6 +53,10 @@
 
       %h1.title= title
 
+      .header-logo
+        = link_to root_path, class: 'home', title: 'Dashboard', id: 'logo' do
+          = brand_header_logo
+
       = yield :header_content
 
 = render 'shared/outdated_browser'
diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml
index 6a027339a1ad4de75d01f8d50afba7f2a6b87f15..16cceb54290ac5a715ea53a30e08a927a3c154f4 100644
--- a/app/views/layouts/nav/_admin.html.haml
+++ b/app/views/layouts/nav/_admin.html.haml
@@ -1,124 +1,77 @@
-%ul.nav.nav-sidebar
-  = nav_link(controller: :dashboard, html_options: {class: 'home'}) do
-    = link_to admin_root_path, title: 'Overview' do
-      = icon('dashboard fw')
+%ul.nav-links.scrolling-tabs
+  .fade-left
+  = nav_link(controller: %w(dashboard admin projects users groups builds), html_options: {class: 'home'}) do
+    = link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do
       %span
         Overview
-  = nav_link(controller: [:admin, :projects]) do
-    = link_to admin_namespaces_projects_path, title: 'Projects' do
-      = icon('cube fw')
+  = nav_link(controller: %w(background_jobs logs health_check)) do
+    = link_to admin_background_jobs_path, title: 'Monitoring' do
       %span
-        Projects
-  = nav_link(controller: :users) do
-    = link_to admin_users_path, title: 'Users' do
-      = icon('user fw')
-      %span
-        Users
-  = nav_link(controller: :groups) do
-    = link_to admin_groups_path, title: 'Groups' do
-      = icon('group fw')
-      %span
-        Groups
+        Monitoring
   = nav_link(controller: :deploy_keys) do
     = link_to admin_deploy_keys_path, title: 'Deploy Keys' do
-      = icon('key fw')
       %span
         Deploy Keys
   = nav_link path: ['runners#index', 'runners#show'] do
     = link_to admin_runners_path, title: 'Runners' do
-      = icon('cog fw')
       %span
         Runners
-        %span.count= number_with_delimiter(Ci::Runner.count(:all))
-  = nav_link path: 'builds#index' do
-    = link_to admin_builds_path, title: 'Builds' do
-      = icon('link fw')
-      %span
-        Builds
-        %span.count= number_with_delimiter(Ci::Build.count(:all))
-  = nav_link(controller: :logs) do
-    = link_to admin_logs_path, title: 'Logs' do
-      = icon('file-text fw')
-      %span
-        Logs
-  = nav_link(controller: :health_check) do
-    = link_to admin_health_check_path, title: 'Health Check' do
-      = icon('medkit fw')
-      %span
-        Health Check
   = nav_link(controller: :broadcast_messages) do
     = link_to admin_broadcast_messages_path, title: 'Messages' do
-      = icon('bullhorn fw')
       %span
         Messages
   = nav_link(controller: :hooks) do
     = link_to admin_hooks_path, title: 'Hooks' do
-      = icon('external-link fw')
       %span
         Hooks
   = nav_link(controller: :git_hooks) do
-    = link_to admin_git_hooks_path, title: 'Git Hooks', data: {placement: 'right'} do
-      = icon('git-square fw')
+    = link_to admin_git_hooks_path, title: 'Git Hooks' do
       %span
         Git Hooks
-  = nav_link(controller: :background_jobs) do
-    = link_to admin_background_jobs_path, title: 'Background Jobs' do
-      = icon('cog fw')
-      %span
-        Background Jobs
   = nav_link(controller: :appearances) do
     = link_to admin_appearances_path, title: 'Appearances' do
-      = icon('image')
       %span
         Appearance
 
   = nav_link(controller: :applications) do
     = link_to admin_applications_path, title: 'Applications' do
-      = icon('cloud fw')
       %span
         Applications
 
   = nav_link(controller: :services) do
     = link_to admin_application_settings_services_path, title: 'Service Templates' do
-      = icon('copy fw')
       %span
         Service Templates
 
   = nav_link(controller: :labels) do
     = link_to admin_labels_path, title: 'Labels' do
-      = icon('tags fw')
       %span
         Labels
 
   = nav_link(controller: :geo_nodes) do
     = link_to admin_geo_nodes_path, title: 'Geo Nodes' do
-      = icon('globe fw')
       %span
         Geo Nodes
 
   = nav_link(controller: :abuse_reports) do
     = link_to admin_abuse_reports_path, title: "Abuse Reports" do
-      = icon('exclamation-circle fw')
       %span
         Abuse Reports
-        %span.count= number_with_delimiter(AbuseReport.count(:all))
+        %span.badge.count= number_with_delimiter(AbuseReport.count(:all))
 
   - if askimet_enabled?
     = nav_link(controller: :spam_logs) do
       = link_to admin_spam_logs_path, title: "Spam Logs" do
-        = icon('exclamation-triangle fw')
         %span
           Spam Logs
-          %span.count= number_with_delimiter(SpamLog.count(:all))
 
   = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
     = link_to admin_application_settings_path, title: 'Settings' do
-      = icon('cogs fw')
       %span
         Settings
 
   = nav_link(controller: :licenses) do
-    = link_to admin_license_path, title: 'License', data: {placement: 'right'} do
-      = icon('check fw')
+    = link_to admin_license_path, title: 'License' do
       %span
         License
+  .fade-right
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index fad4224e94537970c101bcd3e6a7d7c34739dc9d..52e41b1a8576e9305780f0af1cb922d945db5b2d 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -1,55 +1,64 @@
 %ul.nav.nav-sidebar
-  = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: 'home'}) do
-    = link_to dashboard_projects_path, title: 'Projects' do
-      = icon('bookmark fw')
+  = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do
+    = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
+      .icon-container
+        = navbar_icon('project')
       %span
         Projects
   = nav_link(controller: :todos) do
     = link_to dashboard_todos_path, title: 'Todos' do
-      = icon('bell fw')
+      .icon-container
+        = icon('bell fw')
       %span
         Todos
-        %span.count.todos-pending-count= number_with_delimiter(todos_pending_count)
+        %span.count= number_with_delimiter(todos_pending_count)
   = nav_link(path: 'dashboard#activity') do
-    = link_to activity_dashboard_path, class: 'shortcuts-activity', title: 'Activity' do
-      = icon('dashboard fw')
+    = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
+      .icon-container
+        = navbar_icon('activity')
       %span
         Activity
   = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
     = link_to dashboard_groups_path, title: 'Groups' do
-      = icon('group fw')
+      .icon-container
+        = navbar_icon('group')
       %span
         Groups
   = nav_link(controller: 'dashboard/milestones') do
     = link_to dashboard_milestones_path, title: 'Milestones' do
-      = icon('clock-o fw')
+      .icon-container
+        = navbar_icon('milestones')
       %span
         Milestones
   = nav_link(path: 'dashboard#issues') do
-    = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues' do
-      = icon('exclamation-circle fw')
+    = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
+      .icon-container
+        = navbar_icon('issues')
       %span
         Issues
         %span.count= number_with_delimiter(current_user.assigned_issues.opened.count)
   = nav_link(path: 'dashboard#merge_requests') do
-    = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests' do
-      = icon('tasks fw')
+    = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
+      .icon-container
+        = navbar_icon('mr')
       %span
         Merge Requests
         %span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count)
   = nav_link(controller: :snippets) do
     = link_to dashboard_snippets_path, title: 'Snippets' do
-      = icon('clipboard fw')
+      .icon-container
+        = icon('clipboard fw')
       %span
         Snippets
   = nav_link(controller: :help) do
     = link_to help_path, title: 'Help' do
-      = icon('question-circle fw')
+      .icon-container
+        = icon('question-circle fw')
       %span
         Help
-
   = nav_link(html_options: {class: profile_tab_class}) do
     = link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do
-      = icon('user fw')
+      .icon-container
+        = icon('user fw')
       %span
         Profile Settings
diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml
index c5884fb4e211d6b58fa8b43c0a77c279e830c904..f2908a8bbd043d7ca4dcad7adb91ddf97cd120de 100644
--- a/app/views/layouts/nav/_group.html.haml
+++ b/app/views/layouts/nav/_group.html.haml
@@ -1,42 +1,37 @@
-= render 'layouts/nav/group_settings'
+%div{ class: nav_control_class }
+  = render 'layouts/nav/group_settings'
 
-%ul.nav-links
-  = nav_link(path: 'groups#show', html_options: {class: 'home'}) do
-    = link_to group_path(@group), title: 'Home' do
-      = icon('group fw')
-      %span
-        Group
-  = nav_link(path: 'groups#activity') do
-    = link_to activity_group_path(@group), title: 'Activity' do
-      = icon('dashboard fw')
-      %span
-        Activity
-  = nav_link(controller: [:group, :milestones]) do
-    = link_to group_milestones_path(@group), title: 'Milestones' do
-      = icon('clock-o fw')
-      %span
-        Milestones
-  = nav_link(path: 'groups#issues') do
-    = link_to issues_group_path(@group), title: 'Issues' do
-      = icon('exclamation-circle fw')
-      %span
-        Issues
-        - issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
-        %span.badge.count= number_with_delimiter(issues.count)
-  = nav_link(path: 'groups#merge_requests') do
-    = link_to merge_requests_group_path(@group), title: 'Merge Requests' do
-      = icon('tasks fw')
-      %span
-        Merge Requests
-        - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute
-        %span.badge.count= number_with_delimiter(merge_requests.count)
-  = nav_link(controller: [:group_members]) do
-    = link_to group_group_members_path(@group), title: 'Members' do
-      = icon('users fw')
-      %span
-        Members
-  = nav_link(controller: [:stats]) do
-    = link_to group_analytics_path(@group), title: 'Contribution Analytics', data: {placement: 'right'} do
-      = icon('bar-chart fw')
-      %span
-        Contribution Analytics
+  %ul.nav-links.scrolling-tabs
+    .fade-left
+    = nav_link(path: 'groups#show', html_options: {class: 'home'}) do
+      = link_to group_path(@group), title: 'Home' do
+        %span
+          Group
+    = nav_link(path: 'groups#activity') do
+      = link_to activity_group_path(@group), title: 'Activity' do
+        %span
+          Activity
+    = nav_link(controller: [:group, :milestones]) do
+      = link_to group_milestones_path(@group), title: 'Milestones' do
+        %span
+          Milestones
+    = nav_link(path: 'groups#issues') do
+      = link_to issues_group_path(@group), title: 'Issues' do
+        %span
+          Issues
+          - issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
+          %span.badge.count= number_with_delimiter(issues.count)
+    = nav_link(path: 'groups#merge_requests') do
+      = link_to merge_requests_group_path(@group), title: 'Merge Requests' do
+        %span
+          Merge Requests
+          - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute
+          %span.badge.count= number_with_delimiter(merge_requests.count)
+    = nav_link(controller: [:group_members]) do
+      = link_to group_group_members_path(@group), title: 'Members' do
+        %span
+          Members
+    = nav_link(controller: [:stats]) do
+      = link_to group_analytics_path(@group), title: 'Contribution Analytics', data: {placement: 'right'} do
+        %span
+          Contribution Analytics
diff --git a/app/views/layouts/nav/_group_settings.html.haml b/app/views/layouts/nav/_group_settings.html.haml
index 964971df281272346ac1f05cd494fd64c1a0a30d..21332b4159dfdf3c74bfd9cdfb4266413361afd2 100644
--- a/app/views/layouts/nav/_group_settings.html.haml
+++ b/app/views/layouts/nav/_group_settings.html.haml
@@ -27,7 +27,3 @@
             %li
               = link_to edit_group_path(@group) do
                 Edit Group
-          %li
-            = link_to leave_group_group_members_path(@group),
-              data: { confirm: leave_group_message(@group.name) }, method: :delete, title: 'Leave group' do
-              Leave Group
diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml
index d730840d63a9e9b3bfb2383d11b465d87661928e..d4b1f477f3f1deffd2976bd71b252e7b6a3c7780 100644
--- a/app/views/layouts/nav/_profile.html.haml
+++ b/app/views/layouts/nav/_profile.html.haml
@@ -1,49 +1,42 @@
-%ul.nav-links
+%ul.nav-links.scrolling-tabs
+  .fade-left
   = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
     = link_to profile_path, title: 'Profile Settings' do
-      = icon('user fw')
       %span
         Profile
   = nav_link(controller: [:accounts, :two_factor_auths]) do
     = link_to profile_account_path, title: 'Account' do
-      = icon('gear fw')
       %span
         Account
-  = nav_link(controller: 'oauth/applications') do
-    = link_to applications_profile_path, title: 'Applications' do
-      = icon('cloud fw')
-      %span
-        Applications
+  - if current_application_settings.user_oauth_applications?
+    = nav_link(controller: 'oauth/applications') do
+      = link_to applications_profile_path, title: 'Applications' do
+        %span
+          Applications
   = nav_link(controller: :emails) do
     = link_to profile_emails_path, title: 'Emails' do
-      = icon('envelope-o fw')
       %span
         Emails
   - unless current_user.ldap_user?
     = nav_link(controller: :passwords) do
       = link_to edit_profile_password_path, title: 'Password' do
-        = icon('lock fw')
         %span
           Password
   = nav_link(controller: :notifications) do
     = link_to profile_notifications_path, title: 'Notifications' do
-      = icon('inbox fw')
       %span
         Notifications
 
   = nav_link(controller: :keys) do
     = link_to profile_keys_path, title: 'SSH Keys' do
-      = icon('key fw')
       %span
         SSH Keys
   = nav_link(controller: :preferences) do
     = link_to profile_preferences_path, title: 'Preferences' do
-      -# TODO (rspeicher): Better icon?
-      = icon('image fw')
       %span
         Preferences
   = nav_link(path: 'profiles#audit_log') do
     = link_to audit_log_profile_path, title: 'Audit Log' do
-      = icon('history fw')
       %span
         Audit Log
+  .fade-right
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 6dff488eda57fa93a444049fb2554206162bf001..a851cae4b5616f3195a56508aed14f8a9002368b 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -1,146 +1,111 @@
-%ul.nav.nav-sidebar
-  - if @project.group
-    = nav_link do
-      = link_to group_path(@project.group), title: 'Go to group', class: 'back-link' do
-        = icon('caret-square-o-left fw')
-        %span
-          Go to group
-  - else
-    = nav_link do
-      = link_to root_path, title: 'Go to dashboard', class: 'back-link' do
-        = icon('caret-square-o-left fw')
-        %span
-          Go to dashboard
-
-  %li.separate-item
-
-  = nav_link(path: 'projects#show', html_options: {class: 'home'}) do
-    = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
-      = icon('bookmark fw')
-      %span
-        Project
-  = nav_link(path: 'projects#activity') do
-    = link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do
-      = icon('dashboard fw')
-      %span
-        Activity
-  - if project_nav_tab? :files
-    = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
-      = link_to project_files_path(@project), title: 'Files',  class: 'shortcuts-tree' do
-        = icon('files-o fw')
-        %span
-          Files
-
-  - if project_nav_tab? :commits
-    = nav_link(controller: %w(commit commits compare repositories tags branches releases network)) do
-      = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
-        = icon('history fw')
-        %span
-          Commits
-
-  - if project_nav_tab? :pipelines
-    = nav_link(controller: :pipelines) do
-      = link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
-        = icon('ship fw')
-        %span
-          Pipelines
-          %span.count.ci_counter= number_with_delimiter(@project.ci_commits.running_or_pending.count)
+- if current_user
+  .controls
+    .dropdown.project-settings-dropdown
+      %a.dropdown-new.btn.btn-default#project-settings-button{href: '#', 'data-toggle' => 'dropdown'}
+        = icon('cog')
+        = icon('caret-down')
+      %ul.dropdown-menu.dropdown-menu-align-right
+        - access = @project.team.max_member_access(current_user.id)
+        - can_edit = can?(current_user, :admin_project, @project)
+
+        = render 'layouts/nav/project_settings', access: access, can_edit: can_edit
+
+        - if can_edit || access
+          %li.divider
+          - if can_edit
+            %li
+              = link_to edit_project_path(@project) do
+                Edit Project
+          - if access
+            %li
+              = link_to polymorphic_path([:leave, @project, :members]),
+                data: { confirm: leave_confirmation_message(@project) }, method: :delete, title: 'Leave project' do
+                Leave Project
+
+%div{ class: nav_control_class }
+  %ul.nav-links.scrolling-tabs
+    .fade-left
+    = nav_link(path: 'projects#show', html_options: {class: 'home'}) do
+      = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
+        %span
+          Project
+
+    = nav_link(path: 'projects#activity') do
+      = link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do
+        %span
+          Activity
+
+    - if project_nav_tab? :files
+      = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare repositories tags branches releases network)) do
+        = link_to project_files_path(@project), title: 'Code',  class: 'shortcuts-tree' do
+          %span
+            Code
+
+    - if project_nav_tab? :pipelines
+      = nav_link(controller: [:pipelines, :builds, :environments]) do
+        = link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
+          %span
+            Pipelines
+
+    - if project_nav_tab? :container_registry
+      = nav_link(controller: %w(container_registry)) do
+        = link_to project_container_registry_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do
+          %span
+            Registry
+
+    - if project_nav_tab? :graphs
+      = nav_link(controller: %w(graphs)) do
+        = link_to namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Graphs',  class: 'shortcuts-graphs' do
+          %span
+            Graphs
+
+    - if project_nav_tab? :issues
+      = nav_link(controller: [:issues, :labels, :milestones]) do
+        = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues' do
+          %span
+            Issues
+            - if @project.default_issues_tracker?
+              %span.badge.count.issue_counter= number_with_delimiter(@project.issues.visible_to_user(current_user).opened.count)
+
+    - if project_nav_tab? :merge_requests
+      = nav_link(controller: :merge_requests) do
+        = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
+          %span
+            Merge Requests
+            %span.badge.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count)
+
+    - if project_nav_tab? :wiki
+      = nav_link(controller: :wikis) do
+        = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do
+          %span
+            Wiki
+
+    - if project_nav_tab? :snippets
+      = nav_link(controller: :snippets) do
+        = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do
+          %span
+            Snippets
+
+    -# Global shortcut to network page for compatibility
+    - if project_nav_tab? :network
+      %li.hidden
+        = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network' do
+          Network
+
+    -# Shortcut to create a new issue
+    %li.hidden
+      = link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'shortcuts-new-issue' do
+        Create a new issue
 
-  - if project_nav_tab? :builds
-    = nav_link(controller: %w(builds)) do
-      = link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do
-        = icon('cubes fw')
-        %span
+    -# Shortcut to builds page
+    - if project_nav_tab? :builds
+      %li.hidden
+        = link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do
           Builds
-          %span.count.builds_counter= number_with_delimiter(@project.builds.running_or_pending.count(:all))
-
-  - if project_nav_tab? :container_registry
-    = nav_link(controller: %w(container_registry)) do
-      = link_to project_container_registry_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do
-        = icon('hdd-o fw')
-        %span
-          Container Registry
-
-  - if project_nav_tab? :graphs
-    = nav_link(controller: %w(graphs)) do
-      = link_to namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Graphs',  class: 'shortcuts-graphs' do
-        = icon('area-chart fw')
-        %span
-          Graphs
-
-  - if project_nav_tab? :milestones
-    = nav_link(controller: :milestones) do
-      = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do
-        = icon('clock-o fw')
-        %span
-          Milestones
-
-  - if project_nav_tab? :issues
-    = nav_link(controller: :issues) do
-      = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues' do
-        = icon('exclamation-circle fw')
-        %span
-          Issues
-          - if @project.default_issues_tracker?
-            %span.count.issue_counter= number_with_delimiter(@project.issues.visible_to_user(current_user).opened.count)
-
-  - if project_nav_tab? :merge_requests
-    = nav_link(controller: :merge_requests) do
-      = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
-        = icon('tasks fw')
-        %span
-          Merge Requests
-          %span.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count)
 
-  - if project_nav_tab? :team
-    = nav_link(controller: [:project_members, :teams]) do
-      = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do
-        = icon('users fw')
-        %span
-          Members
-
-  - if project_nav_tab? :labels
-    = nav_link(controller: :labels) do
-      = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do
-        = icon('tags fw')
-        %span
-          Labels
-
-  - if project_nav_tab? :wiki
-    = nav_link(controller: :wikis) do
-      = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do
-        = icon('book fw')
-        %span
-          Wiki
-
-  - if project_nav_tab? :forks
-    = nav_link(controller: :forks, action: :index) do
-      = link_to namespace_project_forks_path(@project.namespace, @project), title: 'Forks' do
-        = icon('code-fork fw')
-        %span
-          Forks
-
-  - if project_nav_tab? :snippets
-    = nav_link(controller: :snippets) do
-      = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do
-        = icon('clipboard fw')
-        %span
-          Snippets
-
-  - if project_nav_tab? :settings
-    = nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
-      = link_to edit_project_path(@project), title: 'Settings' do
-        = icon('cogs fw')
-        %span
-          Settings
-
-  -# Global shortcut to network page for compatibility
-  - if project_nav_tab? :network
-    %li.hidden
-      = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network' do
-        Network
-
-  -# Shortcut to create a new issue
-  %li.hidden
-    = link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'shortcuts-new-issue' do
-      Create a new issue
+    -# Shortcut to commits page
+    - if project_nav_tab? :commits
+      %li.hidden
+        = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
+          Commits
+    .fade-right
diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml
index f9a614fc548855e82a4e7ffe8382ebab3d259af6..2d541be7b49cba1b6bf694da53b0f1f7be59dcee 100644
--- a/app/views/layouts/nav/_project_settings.html.haml
+++ b/app/views/layouts/nav/_project_settings.html.haml
@@ -1,83 +1,61 @@
-%ul.nav.nav-sidebar
-  = nav_link do
-    = link_to project_path(@project), title: 'Go to project', class: 'back-link' do
-      = icon('caret-square-o-left fw')
+- if project_nav_tab? :team
+  = nav_link(controller: [:project_members, :teams]) do
+    = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do
       %span
-        Go to project
-
-  %li.separate-item
+        Members
+- if access && can_edit
+  - if @project.allowed_to_share_with_group?
+    = nav_link(controller: :group_links) do
+      = link_to namespace_project_group_links_path(@project.namespace, @project), title: "Groups" do
+        %span
+          Groups
+  = nav_link(controller: :deploy_keys) do
+    = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys' do
+      %span
+        Deploy Keys
+  = nav_link(controller: :hooks) do
+    = link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Webhooks' do
+      %span
+        Webhooks
+  = nav_link(controller: :services) do
+    = link_to namespace_project_services_path(@project.namespace, @project), title: 'Services' do
+      %span
+        Services
+  = nav_link(controller: :protected_branches) do
+    = link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches' do
+      %span
+        Protected Branches
 
-  %ul.sidebar-subnav
-    = nav_link(path: 'projects#edit') do
-      = link_to edit_project_path(@project), title: 'Project Settings' do
-        = icon('pencil-square-o fw')
-        %span
-          Project Settings
-    - if @project.allowed_to_share_with_group?
-      = nav_link(controller: :group_links) do
-        = link_to namespace_project_group_links_path(@project.namespace, @project), title: "Groups" do
-          = icon('share-square-o fw')
-          %span
-            Groups
-    = nav_link(controller: :deploy_keys) do
-      = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys' do
-        = icon('key fw')
-        %span
-          Deploy Keys
-    = nav_link(controller: :hooks) do
-      = link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Webhooks' do
-        = icon('link fw')
-        %span
-          Webhooks
-    = nav_link(controller: :git_hooks) do
-      = link_to namespace_project_git_hooks_path(@project.namespace, @project), title: "Git Hooks" do
-        = icon('git-square fw')
+  - if @project.builds_enabled?
+    = nav_link(controller: :runners) do
+      = link_to namespace_project_runners_path(@project.namespace, @project), title: 'Runners' do
         %span
-          Git Hooks
-    = nav_link(controller: :services) do
-      = link_to namespace_project_services_path(@project.namespace, @project), title: 'Services' do
-        = icon('cogs fw')
+          Runners
+    = nav_link(controller: :variables) do
+      = link_to namespace_project_variables_path(@project.namespace, @project), title: 'Variables' do
         %span
-          Services
-    = nav_link(controller: :protected_branches) do
-      = link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches' do
-        = icon('lock fw')
+          Variables
+    = nav_link(controller: :triggers) do
+      = link_to namespace_project_triggers_path(@project.namespace, @project), title: 'Triggers' do
         %span
-          Protected Branches
-    = nav_link(controller: :mirrors) do
-      = link_to namespace_project_mirror_path(@project.namespace, @project), title: 'Mirror Repository', data: {placement: 'right'} do
-        = icon('clone fw')
+          Triggers
+    = nav_link(controller: :badges) do
+      = link_to namespace_project_badges_path(@project.namespace, @project), title: 'Badges' do
         %span
-          Mirror Repository
-    = nav_link(controller: :pages) do
-      = link_to namespace_project_pages_path(@project.namespace, @project), title: 'Pages', data: {placement: 'right'} do
-        = icon('cloud-upload fw')
-        %span
-          Pages
-    = nav_link(controller: :audit_events) do
-      = link_to namespace_project_audit_events_path(@project.namespace, @project), title: "Audit Events" do
-        = icon('file-text-o fw')
-        %span
-          Audit Events
-
-    - if @project.builds_enabled?
-      = nav_link(controller: :runners) do
-        = link_to namespace_project_runners_path(@project.namespace, @project), title: 'Runners' do
-          = icon('cog fw')
-          %span
-            Runners
-      = nav_link(controller: :variables) do
-        = link_to namespace_project_variables_path(@project.namespace, @project), title: 'Variables' do
-          = icon('code fw')
-          %span
-            Variables
-      = nav_link(controller: :triggers) do
-        = link_to namespace_project_triggers_path(@project.namespace, @project), title: 'Triggers' do
-          = icon('retweet fw')
-          %span
-            Triggers
-      = nav_link(controller: :badges) do
-        = link_to namespace_project_badges_path(@project.namespace, @project), title: 'Badges' do
-          = icon('star-half-empty fw')
-          %span
-            Badges
+          Badges
+  = nav_link(controller: :git_hooks) do
+    = link_to namespace_project_git_hooks_path(@project.namespace, @project), title: "Git Hooks" do
+      %span
+        Git Hooks
+  = nav_link(controller: :mirrors) do
+    = link_to namespace_project_mirror_path(@project.namespace, @project), title: 'Mirror Repository', data: {placement: 'right'} do
+      %span
+        Mirror Repository
+  = nav_link(controller: :pages) do
+    = link_to namespace_project_pages_path(@project.namespace, @project), title: 'Pages', data: {placement: 'right'} do
+      %span
+        Pages
+  = nav_link(controller: :audit_events) do
+    = link_to namespace_project_audit_events_path(@project.namespace, @project), title: "Audit Events" do
+      %span
+        Audit Events
diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml
index 6dfe7fbdae8115046b6b3aaf44bde52583f3682e..2049b204956b9eebdf311aa30dbed241d422e7ff 100644
--- a/app/views/layouts/project.html.haml
+++ b/app/views/layouts/project.html.haml
@@ -1,12 +1,12 @@
 - page_title       @project.name_with_namespace
 - page_description @project.description    unless page_description
 - header_title     project_title(@project) unless header_title
-- sidebar          "project"               unless sidebar
+- nav              "project"
 
 - content_for :scripts_body_top do
   - project = @target_project || @project
-  - if @project_wiki
-    - markdown_preview_path = namespace_project_wikis_markdown_preview_path(project.namespace, project)
+  - if @project_wiki && @page
+    - markdown_preview_path = namespace_project_wiki_markdown_preview_path(project.namespace, project, params[:id])
   - else
     - markdown_preview_path = markdown_preview_namespace_project_path(project.namespace, project)
   - if current_user
diff --git a/app/views/layouts/project_settings.html.haml b/app/views/layouts/project_settings.html.haml
index 59ce38f67bbd861ac8783e60895b00f9e1592f73..4bc94bd132daa3cc911c961a5c000163e3f28559 100644
--- a/app/views/layouts/project_settings.html.haml
+++ b/app/views/layouts/project_settings.html.haml
@@ -1,5 +1,4 @@
 - page_title  "Settings"
-- header_title project_title(@project, "Settings", edit_project_path(@project))
-- sidebar     "project_settings"
+- nav         "project"
 
 = render template: "layouts/project"
diff --git a/app/views/notify/_note_message.html.haml b/app/views/notify/_note_message.html.haml
index 12ded41fbf2ae5727d6fa86405db616700316869..e9c66170877c85dd3dce6489b015eff3273da9ef 100644
--- a/app/views/notify/_note_message.html.haml
+++ b/app/views/notify/_note_message.html.haml
@@ -2,4 +2,4 @@
   %div
     #{link_to @note.author_name, user_url(@note.author)} wrote:
 %div
-  = markdown(@note.note, pipeline: :email)
+  = markdown(@note.note, pipeline: :email, author: @note.author)
diff --git a/app/views/notify/build_fail_email.html.haml b/app/views/notify/build_fail_email.html.haml
index 81d650373122fb964aa2f1fe7bb6822f9c5a9376..4bf7c1f4d64b9ef0b02ae485a21cadee81acf741 100644
--- a/app/views/notify/build_fail_email.html.haml
+++ b/app/views/notify/build_fail_email.html.haml
@@ -10,7 +10,7 @@
 %p
   Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.project.namespace, @build.project, @build.sha)}
 %p
-  Author: #{@build.commit.git_author_name}
+  Author: #{@build.pipeline.git_author_name}
 %p
   Branch: #{@build.ref}
 %p
@@ -18,7 +18,7 @@
 %p
   Job: #{@build.name}
 %p
-  Message: #{@build.commit.git_commit_message}
+  Message: #{@build.pipeline.git_commit_message}
 
 %p
   Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)}
diff --git a/app/views/notify/build_fail_email.text.erb b/app/views/notify/build_fail_email.text.erb
index 675acea60a16d01fab25fd3c59a16b64f5052b5c..9d497983498fa52a8ed3f189a16cf3754b2ebda8 100644
--- a/app/views/notify/build_fail_email.text.erb
+++ b/app/views/notify/build_fail_email.text.erb
@@ -1,11 +1,11 @@
 Build failed for <%= @project.name %>
 
 Status:   <%= @build.status %>
-Commit:   <%= @build.commit.short_sha %>
-Author:   <%= @build.commit.git_author_name %>
+Commit:   <%= @build.pipeline.short_sha %>
+Author:   <%= @build.pipeline.git_author_name %>
 Branch:   <%= @build.ref %>
 Stage:    <%= @build.stage %>
 Job:      <%= @build.name %>
-Message:  <%= @build.commit.git_commit_message %>
+Message:  <%= @build.pipeline.git_commit_message %>
 
 Url:      <%= namespace_project_build_url(@build.project.namespace, @build.project, @build) %>
diff --git a/app/views/notify/build_success_email.html.haml b/app/views/notify/build_success_email.html.haml
index 5d247eb4cf2e2e2b15be8279ca347b7179de2155..252a5b7152c225f69b4fb7f9e5b93d852e2e744a 100644
--- a/app/views/notify/build_success_email.html.haml
+++ b/app/views/notify/build_success_email.html.haml
@@ -10,7 +10,7 @@
 %p
   Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.project.namespace, @build.project, @build.sha)}
 %p
-  Author: #{@build.commit.git_author_name}
+  Author: #{@build.pipeline.git_author_name}
 %p
   Branch: #{@build.ref}
 %p
@@ -18,7 +18,7 @@
 %p
   Job: #{@build.name}
 %p
-  Message: #{@build.commit.git_commit_message}
+  Message: #{@build.pipeline.git_commit_message}
 
 %p
   Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)}
diff --git a/app/views/notify/build_success_email.text.erb b/app/views/notify/build_success_email.text.erb
index 747da44acae8d1e6c62ee9283f95494fea0ed433..c5ed4f84861c0979e5e462acfb6d0d1200250b0e 100644
--- a/app/views/notify/build_success_email.text.erb
+++ b/app/views/notify/build_success_email.text.erb
@@ -1,11 +1,11 @@
 Build successful for <%= @project.name %>
 
 Status:   <%= @build.status %>
-Commit:   <%= @build.commit.short_sha %>
-Author:   <%= @build.commit.git_author_name %>
+Commit:   <%= @build.pipeline.short_sha %>
+Author:   <%= @build.pipeline.git_author_name %>
 Branch:   <%= @build.ref %>
 Stage:    <%= @build.stage %>
 Job:      <%= @build.name %>
-Message:  <%= @build.commit.git_commit_message %>
+Message:  <%= @build.pipeline.git_commit_message %>
 
 Url:      <%= namespace_project_build_url(@build.project.namespace, @build.project, @build) %>
diff --git a/app/views/notify/group_access_granted_email.html.haml b/app/views/notify/group_access_granted_email.html.haml
deleted file mode 100644
index f1916d624b6b35d9fabe1808660022197e8b324d..0000000000000000000000000000000000000000
--- a/app/views/notify/group_access_granted_email.html.haml
+++ /dev/null
@@ -1,4 +0,0 @@
-%p
-  = "You have been granted #{@group_member.human_access} access to group"
-  = link_to group_url(@group) do
-    = @group.name
diff --git a/app/views/notify/group_access_granted_email.text.erb b/app/views/notify/group_access_granted_email.text.erb
deleted file mode 100644
index ef9617bfc16ad3d47aa75524e5d28a379ca1639b..0000000000000000000000000000000000000000
--- a/app/views/notify/group_access_granted_email.text.erb
+++ /dev/null
@@ -1,4 +0,0 @@
-
-You have been granted <%= @group_member.human_access %> access to group <%= @group.name %>
-
-<%= url_for(group_url(@group)) %>
diff --git a/app/views/notify/group_invite_accepted_email.html.haml b/app/views/notify/group_invite_accepted_email.html.haml
deleted file mode 100644
index 55efad384a79552ba51ee49935e04fcdc9a53c1e..0000000000000000000000000000000000000000
--- a/app/views/notify/group_invite_accepted_email.html.haml
+++ /dev/null
@@ -1,6 +0,0 @@
-%p
-  #{@group_member.invite_email}, now known as
-  #{link_to @group_member.user.name, user_url(@group_member.user)},
-  has accepted your invitation to join group
-  #{link_to @group.name, group_url(@group)}.
-
diff --git a/app/views/notify/group_invite_accepted_email.text.erb b/app/views/notify/group_invite_accepted_email.text.erb
deleted file mode 100644
index f8b70f7a5a60c8d617cd94e3db88a91da13c8007..0000000000000000000000000000000000000000
--- a/app/views/notify/group_invite_accepted_email.text.erb
+++ /dev/null
@@ -1,3 +0,0 @@
-<%= @group_member.invite_email %>, now known as <%= @group_member.user.name %>, has accepted your invitation to join group <%= @group.name %>.
-
-<%= group_url(@group) %>
diff --git a/app/views/notify/group_invite_declined_email.html.haml b/app/views/notify/group_invite_declined_email.html.haml
deleted file mode 100644
index f9525d84fac655d96f2dbaced015467d4bcad1fa..0000000000000000000000000000000000000000
--- a/app/views/notify/group_invite_declined_email.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-%p
-  #{@invite_email}
-  has declined your invitation to join group
-  #{link_to @group.name, group_url(@group)}.
-
diff --git a/app/views/notify/group_invite_declined_email.text.erb b/app/views/notify/group_invite_declined_email.text.erb
deleted file mode 100644
index 6c19a288d15fb2030a5c814645fbd4484156c276..0000000000000000000000000000000000000000
--- a/app/views/notify/group_invite_declined_email.text.erb
+++ /dev/null
@@ -1,3 +0,0 @@
-<%= @invite_email %> has declined your invitation to join group <%= @group.name %>.
-
-<%= group_url(@group) %>
diff --git a/app/views/notify/group_member_invited_email.html.haml b/app/views/notify/group_member_invited_email.html.haml
deleted file mode 100644
index 163e88bfea3b455a6fd478ed25236f703d681372..0000000000000000000000000000000000000000
--- a/app/views/notify/group_member_invited_email.html.haml
+++ /dev/null
@@ -1,14 +0,0 @@
-%p
-  You have been invited
-  - if inviter = @group_member.created_by
-    by
-    = link_to inviter.name, user_url(inviter)
-  to join group
-  = link_to @group.name, group_url(@group)
-  as #{@group_member.human_access}.
-
-%p
-  = link_to 'Accept invitation', invite_url(@token)
-  or
-  = link_to 'decline', decline_invite_url(@token)
-
diff --git a/app/views/notify/group_member_invited_email.text.erb b/app/views/notify/group_member_invited_email.text.erb
deleted file mode 100644
index 28ce4819b14eedf5132758b9e88db81bb6d10940..0000000000000000000000000000000000000000
--- a/app/views/notify/group_member_invited_email.text.erb
+++ /dev/null
@@ -1,4 +0,0 @@
-You have been invited <%= "by #{@group_member.created_by.name} " if @group_member.created_by %>to join group <%= @group.name %> as <%= @group_member.human_access %>.
-
-Accept invitation: <%= invite_url(@token) %>
-Decline invitation: <%= decline_invite_url(@token) %>
diff --git a/app/views/notify/member_access_denied_email.html.haml b/app/views/notify/member_access_denied_email.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..71c9c50071a0ada253a2a16775e22c623e62c5a3
--- /dev/null
+++ b/app/views/notify/member_access_denied_email.html.haml
@@ -0,0 +1,4 @@
+%p
+  Your request to join the
+  #{link_to member_source.human_name, member_source.web_url} #{member_source.model_name.singular}
+  has been denied.
diff --git a/app/views/notify/member_access_denied_email.text.erb b/app/views/notify/member_access_denied_email.text.erb
new file mode 100644
index 0000000000000000000000000000000000000000..87f2ef817eee25ba18e61fbba454ff2c11a062ec
--- /dev/null
+++ b/app/views/notify/member_access_denied_email.text.erb
@@ -0,0 +1,3 @@
+Your request to join the <%= member_source.human_name %> <%= member_source.model_name.singular %> has been denied.
+
+<%= member_source.web_url %>
diff --git a/app/views/notify/member_access_granted_email.html.haml b/app/views/notify/member_access_granted_email.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..18dec8065393c01d1ddbffbf4eb476eac67f3bcf
--- /dev/null
+++ b/app/views/notify/member_access_granted_email.html.haml
@@ -0,0 +1,3 @@
+%p
+  You have been granted #{member.human_access} access to the
+  #{link_to member_source.human_name, member_source.web_url} #{member_source.model_name.singular}.
diff --git a/app/views/notify/member_access_granted_email.text.erb b/app/views/notify/member_access_granted_email.text.erb
new file mode 100644
index 0000000000000000000000000000000000000000..a9fb3a589a51ad57b05a3a432874fea1a28221e0
--- /dev/null
+++ b/app/views/notify/member_access_granted_email.text.erb
@@ -0,0 +1,3 @@
+You have been granted <%= member.human_access %> access to the <%= member_source.human_name %> <%= member_source.model_name.singular %>.
+
+<%= member_source.web_url %>
diff --git a/app/views/notify/member_access_requested_email.html.haml b/app/views/notify/member_access_requested_email.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..76f1f08a0cbfe9fdbcd2e2b9072ea13e1c0a83f4
--- /dev/null
+++ b/app/views/notify/member_access_requested_email.html.haml
@@ -0,0 +1,3 @@
+%p
+  #{link_to member.user.name, member.user} requested #{member.human_access}
+  access to the #{link_to member_source.human_name, polymorphic_url([member_source, :members])} #{member_source.model_name.singular}.
diff --git a/app/views/notify/member_access_requested_email.text.erb b/app/views/notify/member_access_requested_email.text.erb
new file mode 100644
index 0000000000000000000000000000000000000000..9c5ee0eaf26a4a9f4c05d2e164c680f985b6aa1a
--- /dev/null
+++ b/app/views/notify/member_access_requested_email.text.erb
@@ -0,0 +1,3 @@
+<%= member.user.name %> (<%= user_url(member.user) %>) requested <%= member.human_access %> access to the <%= member_source.human_name %> <%= member_source.model_name.singular %>.
+
+<%= polymorphic_url([member_source, :members]) %>
diff --git a/app/views/notify/member_invite_accepted_email.html.haml b/app/views/notify/member_invite_accepted_email.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..2d1d40881ebc7f98660d7bbfdcf82d783c7ebbb0
--- /dev/null
+++ b/app/views/notify/member_invite_accepted_email.html.haml
@@ -0,0 +1,5 @@
+%p
+  #{member.invite_email}, now known as
+  #{link_to member.user.name, user_url(member.user)},
+  has accepted your invitation to join the
+  #{link_to member_source.human_name, member_source.web_url} #{member_source.model_name.singular}.
diff --git a/app/views/notify/member_invite_accepted_email.text.erb b/app/views/notify/member_invite_accepted_email.text.erb
new file mode 100644
index 0000000000000000000000000000000000000000..cef87101427e3310fc4d43f9cf10ef04325794cc
--- /dev/null
+++ b/app/views/notify/member_invite_accepted_email.text.erb
@@ -0,0 +1,3 @@
+<%= member.invite_email %>, now known as <%= member.user.name %>, has accepted your invitation to join the <%= member_source.human_name %> <%= member_source.model_name.singular %>.
+
+<%= member_source.web_url %>
diff --git a/app/views/notify/member_invite_declined_email.html.haml b/app/views/notify/member_invite_declined_email.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..aa1b373d1a630d5bc97c23c1c2b13c7981cd7d32
--- /dev/null
+++ b/app/views/notify/member_invite_declined_email.html.haml
@@ -0,0 +1,4 @@
+%p
+  #{@invite_email}
+  has declined your invitation to join the
+  #{link_to member_source.human_name, member_source.web_url} #{member_source.model_name.singular}.
diff --git a/app/views/notify/member_invite_declined_email.text.erb b/app/views/notify/member_invite_declined_email.text.erb
new file mode 100644
index 0000000000000000000000000000000000000000..8bc305910c49c1c6f16335b8c34433fa911716cc
--- /dev/null
+++ b/app/views/notify/member_invite_declined_email.text.erb
@@ -0,0 +1,3 @@
+<%= @invite_email %> has declined your invitation to join the <%= member_source.human_name %> <%= member_source.model_name.singular %>.
+
+<%= member_source.web_url %>
diff --git a/app/views/notify/member_invited_email.html.haml b/app/views/notify/member_invited_email.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..b8b75da3f2f02d47d0a77b5a6dc2856788a2797e
--- /dev/null
+++ b/app/views/notify/member_invited_email.html.haml
@@ -0,0 +1,13 @@
+%p
+  You have been invited
+  - if member.created_by
+    by
+    = link_to member.created_by.name, user_url(member.created_by)
+  to join the
+  = link_to member_source.human_name, member_source.web_url
+  #{member_source.model_name.singular} as #{member.human_access}.
+
+%p
+  = link_to 'Accept invitation', invite_url(@token)
+  or
+  = link_to 'decline', decline_invite_url(@token)
diff --git a/app/views/notify/member_invited_email.text.erb b/app/views/notify/member_invited_email.text.erb
new file mode 100644
index 0000000000000000000000000000000000000000..0a6393355be535c32ff49159e51ed7f413291dc1
--- /dev/null
+++ b/app/views/notify/member_invited_email.text.erb
@@ -0,0 +1,4 @@
+You have been invited <%= "by #{member.created_by.name} " if member.created_by %>to join the <%= member_source.human_name %> <%= member_source.model_name.singular %> as <%= member.human_access %>.
+
+Accept invitation: <%= invite_url(@token) %>
+Decline invitation: <%= decline_invite_url(@token) %>
diff --git a/app/views/notify/new_issue_email.html.haml b/app/views/notify/new_issue_email.html.haml
index ad3ab2525bbfca03382c69a93a601f3b371e3c89..f42b150c0d60cd2014e0dab66d76c1fe34fe96ff 100644
--- a/app/views/notify/new_issue_email.html.haml
+++ b/app/views/notify/new_issue_email.html.haml
@@ -2,7 +2,7 @@
   %div
     #{link_to @issue.author_name, user_url(@issue.author)} wrote:
 -if @issue.description
-  = markdown(@issue.description, pipeline: :email)
+  = markdown(@issue.description, pipeline: :email, author: @issue.author)
 
 - if @issue.assignee_id.present?
   %p
diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml
index fd00a26feb835b99c257403b40e5709253eae9bf..6a590a2b4f36490ee486b1f624669e8ef99d2ae0 100644
--- a/app/views/notify/new_merge_request_email.html.haml
+++ b/app/views/notify/new_merge_request_email.html.haml
@@ -13,4 +13,4 @@
     Approvers: #{render_items_list(@merge_request.approvers_left.map(&:name))}
 
 -if @merge_request.description
-  = markdown(@merge_request.description, pipeline: :email)
+  = markdown(@merge_request.description, pipeline: :email, author: @merge_request.author)
diff --git a/app/views/notify/project_access_granted_email.html.haml b/app/views/notify/project_access_granted_email.html.haml
deleted file mode 100644
index dfc30a2d360cd312cfdb959731994ea6cafa8138..0000000000000000000000000000000000000000
--- a/app/views/notify/project_access_granted_email.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-%p
-  = "You have been granted #{@project_member.human_access} access to project"
-%p
-  = link_to namespace_project_url(@project.namespace, @project) do
-    = @project.name_with_namespace
diff --git a/app/views/notify/project_access_granted_email.text.erb b/app/views/notify/project_access_granted_email.text.erb
deleted file mode 100644
index 68eb1611ba7e6f4e73faa8d09ca8de7d01892ba9..0000000000000000000000000000000000000000
--- a/app/views/notify/project_access_granted_email.text.erb
+++ /dev/null
@@ -1,4 +0,0 @@
-
-You have been granted <%= @project_member.human_access %> access to project <%= @project.name_with_namespace %>
-
-<%= url_for(namespace_project_url(@project.namespace, @project)) %>
diff --git a/app/views/notify/project_invite_accepted_email.html.haml b/app/views/notify/project_invite_accepted_email.html.haml
deleted file mode 100644
index 7e58d30b10a89eaca450b4ee0cae8cd451dd1eff..0000000000000000000000000000000000000000
--- a/app/views/notify/project_invite_accepted_email.html.haml
+++ /dev/null
@@ -1,6 +0,0 @@
-%p
-  #{@project_member.invite_email}, now known as
-  #{link_to @project_member.user.name, user_url(@project_member.user)},
-  has accepted your invitation to join project
-  #{link_to @project.name_with_namespace, namespace_project_url(@project.namespace, @project)}.
-
diff --git a/app/views/notify/project_invite_accepted_email.text.erb b/app/views/notify/project_invite_accepted_email.text.erb
deleted file mode 100644
index fcbe752114dace69c69679b299f93cf1e3672751..0000000000000000000000000000000000000000
--- a/app/views/notify/project_invite_accepted_email.text.erb
+++ /dev/null
@@ -1,3 +0,0 @@
-<%= @project_member.invite_email %>, now known as <%= @project_member.user.name %>, has accepted your invitation to join project <%= @project.name_with_namespace %>.
-
-<%= namespace_project_url(@project.namespace, @project) %>
diff --git a/app/views/notify/project_invite_declined_email.html.haml b/app/views/notify/project_invite_declined_email.html.haml
deleted file mode 100644
index c2d7e6f6e3a0aed662a768042970f210c80099e2..0000000000000000000000000000000000000000
--- a/app/views/notify/project_invite_declined_email.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-%p
-  #{@invite_email}
-  has declined your invitation to join project
-  #{link_to @project.name_with_namespace, namespace_project_url(@project.namespace, @project)}.
-
diff --git a/app/views/notify/project_invite_declined_email.text.erb b/app/views/notify/project_invite_declined_email.text.erb
deleted file mode 100644
index 484687fa51cbeef4ea3527d3b96e62c3e6bc2117..0000000000000000000000000000000000000000
--- a/app/views/notify/project_invite_declined_email.text.erb
+++ /dev/null
@@ -1,3 +0,0 @@
-<%= @invite_email %> has declined your invitation to join project <%= @project.name_with_namespace %>.
-
-<%= namespace_project_url(@project.namespace, @project) %>
diff --git a/app/views/notify/project_member_invited_email.html.haml b/app/views/notify/project_member_invited_email.html.haml
deleted file mode 100644
index 79eb89616de49402bfdde544b88fcfa633248b10..0000000000000000000000000000000000000000
--- a/app/views/notify/project_member_invited_email.html.haml
+++ /dev/null
@@ -1,13 +0,0 @@
-%p
-  You have been invited 
-  - if inviter = @project_member.created_by
-    by
-    = link_to inviter.name, user_url(inviter)
-  to join project
-  = link_to @project.name_with_namespace, namespace_project_url(@project.namespace, @project)
-  as #{@project_member.human_access}.
-
-%p
-  = link_to 'Accept invitation', invite_url(@token)
-  or
-  = link_to 'decline', decline_invite_url(@token)
diff --git a/app/views/notify/project_member_invited_email.text.erb b/app/views/notify/project_member_invited_email.text.erb
deleted file mode 100644
index e07062721158bb29e0a0131e03dd912224d708ff..0000000000000000000000000000000000000000
--- a/app/views/notify/project_member_invited_email.text.erb
+++ /dev/null
@@ -1,4 +0,0 @@
-You have been invited <%= "by #{@project_member.created_by.name} " if @project_member.created_by %>to join project <%= @project.name_with_namespace %> as <%= @project_member.human_access %>.
-
-Accept invitation: <%= invite_url(@token) %>
-Decline invitation: <%= decline_invite_url(@token) %>
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index afd3d79321f045196517adf9cc53b5796e794902..3d2a245ecbdf85279bf10ac51f4ba087d845be16 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -11,7 +11,7 @@
     %p
       Your private token is used to access application resources without authentication.
   .col-lg-9
-    = form_for @user, url: reset_private_token_profile_path, method: :put, html: {class: "private-token"} do |f|
+    = form_for @user, url: reset_private_token_profile_path, method: :put, html: { class: "private-token" } do |f|
       %p.cgray
         - if current_user.private_token
           = label_tag "token", "Private token", class: "label-light"
@@ -29,21 +29,22 @@
 .row.prepend-top-default
   .col-lg-3.profile-settings-sidebar
     %h4.prepend-top-0
-      Two-factor Authentication
+      Two-Factor Authentication
     %p
-      Increase your account's security by enabling two-factor authentication (2FA).
+      Increase your account's security by enabling Two-Factor Authentication (2FA).
   .col-lg-9
     %p
-      Status: #{current_user.two_factor_enabled? ? 'enabled' : 'disabled'}
-    - if !current_user.two_factor_enabled?
-      %p
-        Download the Google Authenticator application from App Store for iOS or Google Play for Android and scan this code.
-        More information is available in the #{link_to('documentation', help_page_path('profile', 'two_factor_authentication'))}.
-      .append-bottom-10
-        = link_to 'Enable two-factor authentication', new_profile_two_factor_auth_path, class: 'btn btn-success'
+      Status: #{current_user.two_factor_enabled? ? 'Enabled' : 'Disabled'}
+    - if current_user.two_factor_enabled?
+      = link_to 'Manage Two-Factor Authentication', profile_two_factor_auth_path, class: 'btn btn-info'
+      = link_to 'Disable', profile_two_factor_auth_path,
+                method: :delete,
+                data: { confirm: "Are you sure? This will invalidate your registered applications and U2F devices." },
+                class: 'btn btn-danger'
     - else
-      = link_to 'Disable Two-factor Authentication', profile_two_factor_auth_path, method: :delete, class: 'btn btn-danger',
-              data: { confirm: 'Are you sure?' }
+      .append-bottom-10
+        = link_to 'Enable Two-Factor Authentication', profile_two_factor_auth_path, class: 'btn btn-success'
+
 %hr
 - if button_based_providers.any?
   .row.prepend-top-default
@@ -70,7 +71,7 @@
 - if current_user.can_change_username?
   .row.prepend-top-default
     .col-lg-3.profile-settings-sidebar
-      %h4.prepend-top-0.change-username-title
+      %h4.prepend-top-0.warning-title
         Change username
       %p
         Changing your username will change path to all personal projects!
@@ -94,7 +95,7 @@
 - if signup_enabled?
   .row.prepend-top-default
     .col-lg-3.profile-settings-sidebar
-      %h4.prepend-top-0.remove-account-title
+      %h4.prepend-top-0.danger-title
         Remove account
     .col-lg-9
       - if @user.can_be_removed?
diff --git a/app/views/profiles/notifications/_group_settings.html.haml b/app/views/profiles/notifications/_group_settings.html.haml
index 89ae7ffda2bb7eb0db802cc90aeb7f468935eca3..f0cf82afe831d03a419640e66aadfaf1a5f86a38 100644
--- a/app/views/profiles/notifications/_group_settings.html.haml
+++ b/app/views/profiles/notifications/_group_settings.html.haml
@@ -1,7 +1,7 @@
 %li.notification-list-item
   %span.notification.fa.fa-holder.append-right-5
     - if setting.global?
-      = notification_icon(current_user.notification_level)
+      = notification_icon(current_user.global_notification_setting.level)
     - else
       = notification_icon(setting.level)
 
diff --git a/app/views/profiles/notifications/_project_settings.html.haml b/app/views/profiles/notifications/_project_settings.html.haml
index 17c097154da66b771198cd22ff1b079985dfdf50..e0fad555c098a897b309fe42c4182f6d3748e1e3 100644
--- a/app/views/profiles/notifications/_project_settings.html.haml
+++ b/app/views/profiles/notifications/_project_settings.html.haml
@@ -1,7 +1,7 @@
 %li.notification-list-item
   %span.notification.fa.fa-holder.append-right-5
     - if setting.global?
-      = notification_icon(current_user.notification_level)
+      = notification_icon(current_user.global_notification_setting.level)
     - else
       = notification_icon(setting.level)
 
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index 7696f112bb3a8a0e763679333103808457b005d7..f2659ac14b54e37c57131881132040b4b7b375a5 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -26,33 +26,7 @@
           = f.select :notification_email, @user.all_emails, { include_blank: false }, class: "select2"
         .form-group
           = f.label :notification_level, class: 'label-light'
-          .radio
-            = f.label :notification_level, value: :disabled do
-              = f.radio_button :notification_level, :disabled
-              .level-title
-                Disabled
-              %p You will not get any notifications via email
-
-          .radio
-            = f.label :notification_level, value: :mention do
-              = f.radio_button :notification_level, :mention
-              .level-title
-                On Mention
-              %p You will receive notifications only for comments in which you were @mentioned
-
-          .radio
-            = f.label :notification_level, value: :participating do
-              = f.radio_button :notification_level, :participating
-              .level-title
-                Participating
-              %p You will only receive notifications from related resources (e.g. from your commits or assigned issues)
-
-          .radio
-            = f.label :notification_level, value: :watch do
-              = f.radio_button :notification_level, :watch
-              .level-title
-                Watch
-              %p You will receive notifications for any activity
+          = notification_level_radio_buttons
 
         .prepend-top-default
           = f.submit 'Update settings', class: "btn btn-create"
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index bfe53be6854c0ac1c725be966bc11bc1884eff95..1b1b16d656f1edf3bae37f40d3b6bb3e1f4df1fa 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -5,7 +5,7 @@
     %h4.prepend-top-0
       Application theme
     %p
-      This setting allows you to customize the appearance of the site, ex. sidebar.
+      This setting allows you to customize the appearance of the site, e.g. the sidebar.
   .col-lg-9.application-theme
     - Gitlab::Themes.each do |theme|
       = label_tag do
diff --git a/app/views/profiles/two_factor_auths/new.html.haml b/app/views/profiles/two_factor_auths/new.html.haml
deleted file mode 100644
index 69fc81cb45c66327b7d54b400ad9fc2ae3659f1a..0000000000000000000000000000000000000000
--- a/app/views/profiles/two_factor_auths/new.html.haml
+++ /dev/null
@@ -1,39 +0,0 @@
-- page_title 'Two-factor Authentication', 'Account'
-
-.row.prepend-top-default
-  .col-lg-3
-    %h4.prepend-top-0
-      Two-factor Authentication (2FA)
-    %p
-      Increase your account's security by enabling two-factor authentication (2FA).
-  .col-lg-9
-    %p
-      Download the Google Authenticator application from App Store for iOS or Google Play for Android and scan this code.
-      More information is available in the #{link_to('documentation', help_page_path('profile', 'two_factor_authentication'))}.
-    .row.append-bottom-10
-      .col-md-3
-        = raw @qr_code
-      .col-md-9
-        .account-well
-          %p.prepend-top-0.append-bottom-0
-            Can't scan the code?
-          %p.prepend-top-0.append-bottom-0
-            To add the entry manually, provide the following details to the application on your phone.
-          %p.prepend-top-0.append-bottom-0
-            Account:
-            = current_user.email
-          %p.prepend-top-0.append-bottom-0
-            Key:
-            = current_user.otp_secret.scan(/.{4}/).join(' ')
-          %p.two-factor-new-manual-content
-            Time based: Yes
-    = form_tag profile_two_factor_auth_path, method: :post do |f|
-      - if @error
-        .alert.alert-danger
-          = @error
-      .form-group
-        = label_tag :pin_code, nil, class: "label-light"
-        = text_field_tag :pin_code, nil, class: "form-control", required: true
-      .prepend-top-default
-        = submit_tag 'Enable two-factor authentication', class: 'btn btn-success'
-        = link_to 'Configure it later', skip_profile_two_factor_auth_path, :method => :patch,  class: 'btn btn-cancel' if two_factor_skippable?
diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..593be2617c15cc03aa761680a4dd93887e59ae66
--- /dev/null
+++ b/app/views/profiles/two_factor_auths/show.html.haml
@@ -0,0 +1,69 @@
+- page_title 'Two-Factor Authentication', 'Account'
+- header_title "Two-Factor Authentication", profile_two_factor_auth_path
+
+.row.prepend-top-default
+  .col-lg-3
+    %h4.prepend-top-0
+      Register Two-Factor Authentication App
+    %p
+      Use an app on your mobile device to enable two-factor authentication (2FA).
+  .col-lg-9
+    - if current_user.two_factor_otp_enabled?
+      = icon "check inverse", base: "circle", class: "text-success", text: "You've already enabled two-factor authentication using mobile authenticator applications. You can disable it from your account settings page."
+    - else
+      %p
+        Download the Google Authenticator application from App Store or Google Play Store and scan this code.
+        More information is available in the #{link_to('documentation', help_page_path('profile', 'two_factor_authentication'))}.
+      .row.append-bottom-10
+        .col-md-3
+          = raw @qr_code
+        .col-md-9
+          .account-well
+            %p.prepend-top-0.append-bottom-0
+              Can't scan the code?
+            %p.prepend-top-0.append-bottom-0
+              To add the entry manually, provide the following details to the application on your phone.
+            %p.prepend-top-0.append-bottom-0
+              Account:
+              = current_user.email
+            %p.prepend-top-0.append-bottom-0
+              Key:
+              = current_user.otp_secret.scan(/.{4}/).join(' ')
+            %p.two-factor-new-manual-content
+              Time based: Yes
+      = form_tag profile_two_factor_auth_path, method: :post do |f|
+        - if @error
+          .alert.alert-danger
+            = @error
+        .form-group
+          = label_tag :pin_code, nil, class: "label-light"
+          = text_field_tag :pin_code, nil, class: "form-control", required: true
+        .prepend-top-default
+          = submit_tag 'Register with Two-Factor App', class: 'btn btn-success'
+
+%hr
+
+.row.prepend-top-default
+
+  .col-lg-3
+    %h4.prepend-top-0
+      Register Universal Two-Factor (U2F) Device
+    %p
+      Use a hardware device to add the second factor of authentication.
+    %p
+      As U2F devices are only supported by a few browsers, we require that you set up a
+      two-factor authentication app before a U2F device. That way you'll always be able to
+      log in - even when you're using an unsupported browser.
+  .col-lg-9
+    %p
+      - if @registration_key_handles.present?
+        = icon "check inverse", base: "circle", class: "text-success", text: "You have #{pluralize(@registration_key_handles.size, 'U2F device')} registered with GitLab."
+    - if @u2f_registration.errors.present?
+      = form_errors(@u2f_registration)
+    = render "u2f/register"
+
+- if two_factor_skippable?
+  :javascript
+    var button = "<a class='btn btn-xs btn-warning pull-right' data-method='patch' href='#{skip_profile_two_factor_auth_path}'>Configure it later</a>";
+    $(".flash-alert").append(button);
+
diff --git a/app/views/projects/_builds_settings.html.haml b/app/views/projects/_builds_settings.html.haml
index 637074cc50f37b2b0530e47ab746e98003684c71..2edfa6a06dca92b3166dd6bba4ec7da2bbf3a90e 100644
--- a/app/views/projects/_builds_settings.html.haml
+++ b/app/views/projects/_builds_settings.html.haml
@@ -1,77 +1,69 @@
 %fieldset.builds-feature
-  %legend
-    Builds:
-
+  %h5.prepend-top-0
+    Builds
   - unless @repository.gitlab_ci_yml
     .form-group
-      .col-sm-offset-2.col-sm-10
-        %p Builds need to be configured before you can begin using Continuous Integration.
-        = link_to 'Get started with Builds', help_page_path('ci/quick_start', 'README'), class: 'btn btn-info'
-        %hr
-
+      %p Builds need to be configured before you can begin using Continuous Integration.
+      = link_to 'Get started with Builds', help_page_path('ci/quick_start', 'README'), class: 'btn btn-info'
   .form-group
-    .col-sm-offset-2.col-sm-10
-      %p Get recent application code using the following command:
-      .radio
-        = f.label :build_allow_git_fetch_false do
-          = f.radio_button :build_allow_git_fetch, 'false'
-          %strong git clone
-          %br
-          %span.descr Slower but makes sure you have a clean dir before every build
-      .radio
-        = f.label :build_allow_git_fetch_true do
-          = f.radio_button :build_allow_git_fetch, 'true'
-          %strong git fetch
-          %br
-          %span.descr Faster
+    %p Get recent application code using the following command:
+    .radio
+      = f.label :build_allow_git_fetch_false do
+        = f.radio_button :build_allow_git_fetch, 'false'
+        %strong git clone
+        %br
+        %span.descr Slower but makes sure you have a clean dir before every build
+    .radio
+      = f.label :build_allow_git_fetch_true do
+        = f.radio_button :build_allow_git_fetch, 'true'
+        %strong git fetch
+        %br
+        %span.descr Faster
 
   .form-group
-    = f.label :build_timeout_in_minutes, 'Timeout', class: 'control-label'
-    .col-sm-10
-      = f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0'
-      %p.help-block per build in minutes
+    = f.label :build_timeout_in_minutes, 'Timeout', class: 'label-light'
+    = f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0'
+    %p.help-block per build in minutes
   .form-group
-    = f.label :build_coverage_regex, "Test coverage parsing", class: 'control-label'
-    .col-sm-10
-      .input-group
-        %span.input-group-addon /
-        = f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered'
-        %span.input-group-addon /
-      %p.help-block
-        We will use this regular expression to find test coverage output in build trace.
-        Leave blank if you want to disable this feature
-      .bs-callout.bs-callout-info
-        %p Below are examples of regex for existing tools:
-        %ul
-          %li
-            Simplecov (Ruby) -
-            %code \(\d+.\d+\%\) covered
-          %li
-            pytest-cov (Python) -
-            %code \d+\%\s*$
-          %li
-            phpunit --coverage-text --colors=never (PHP) -
-            %code ^\s*Lines:\s*\d+.\d+\%
-          %li
-            gcovr (C/C++) -
-            %code ^TOTAL.*\s+(\d+\%)$
-          %li
-            tap --coverage-report=text-summary (Node.js) -
-            %code ^Statements\s*:\s*([^%]+)
+    = f.label :build_coverage_regex, "Test coverage parsing", class: 'label-light'
+    .input-group
+      %span.input-group-addon /
+      = f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered'
+      %span.input-group-addon /
+    %p.help-block
+      We will use this regular expression to find test coverage output in build trace.
+      Leave blank if you want to disable this feature
+    .bs-callout.bs-callout-info
+      %p Below are examples of regex for existing tools:
+      %ul
+        %li
+          Simplecov (Ruby) -
+          %code \(\d+.\d+\%\) covered
+        %li
+          pytest-cov (Python) -
+          %code \d+\%\s*$
+        %li
+          phpunit --coverage-text --colors=never (PHP) -
+          %code ^\s*Lines:\s*\d+.\d+\%
+        %li
+          gcovr (C/C++) -
+          %code ^TOTAL.*\s+(\d+\%)$
+        %li
+          tap --coverage-report=text-summary (Node.js) -
+          %code ^Statements\s*:\s*([^%]+)
 
   .form-group
-    .col-sm-offset-2.col-sm-10
-      .checkbox
-        = f.label :public_builds do
-          = f.check_box :public_builds
-          %strong Public builds
-        .help-block Allow everyone to access builds for Public and Internal projects
+    .checkbox
+      = f.label :public_builds do
+        = f.check_box :public_builds
+        %strong Public builds
+      .help-block Allow everyone to access builds for Public and Internal projects
 
   - if @project.mirror?
     = render 'shared/mirror_trigger_builds_setting', f: f
 
-  .form-group
-    = f.label :runners_token, "Runners token", class: 'control-label'
-    .col-sm-10
-      = f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89'
-      %p.help-block The secure token used to checkout project.
+  .form-group.append-bottom-0
+    = f.label :runners_token, "Runners token", class: 'label-light'
+    = f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89'
+    %p.help-block The secure token used to checkout project.
+
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 74670a160e3f926f22a7435e33404d09b3c77097..0db44b45182f3ce07d943741b0d00aa2463d55a4 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -1,68 +1,49 @@
 - empty_repo = @project.empty_repo?
 .project-home-panel.cover-block.clearfix{:class => ("empty-project" if empty_repo)}
-  .project-identicon-holder
-    = project_icon(@project, alt: '', class: 'project-avatar avatar s90')
-  .cover-title.project-home-desc
-    %h1
-      = @project.name
-      %span.visibility-icon.has-tooltip{data: { container: 'body' }, title: visibility_icon_description(@project)}
-        = visibility_level_icon(@project.visibility_level, fw: false)
-
-  - if @project.description.present?
-    .cover-desc.project-home-desc
-      = markdown(@project.description, pipeline: :description)
-
-  - if forked_from_project = @project.forked_from_project
-    .cover-desc
-      Forked from
-      = link_to project_path(forked_from_project) do
-        = forked_from_project.namespace.try(:name)
-
-    - if @project.mirror?
-      - import_url = @project.safe_import_url
-      %p
-        Mirrored from #{link_to import_url, import_url}.
-
-        = render "shared/mirror_status"
-
-  .cover-controls
-    - if current_user
-      = link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), class: 'btn btn-gray' do
-        = icon('rss')
-      - access = user_max_access_in_project(current_user.id, @project)
-      - can_edit = can?(current_user, :admin_project, @project)
-      - if access || can_edit
-        %span.dropdown.project-settings-dropdown
-          %a.dropdown-new.btn.btn-gray#project-settings-button{href: '#', 'data-toggle' => 'dropdown'}
-            = icon('cog')
-            = icon('angle-down')
-          %ul.dropdown-menu.dropdown-menu-right
-            - if can_edit
-              %li
-                = link_to edit_project_path(@project) do
-                  Edit Project
-            - if access
-              %li
-                = link_to leave_namespace_project_project_members_path(@project.namespace, @project),
-                  data: { confirm: leave_project_message(@project) }, method: :delete, title: 'Leave project' do
-                  Leave Project
-
-  .project-repo-buttons
-    .split-one.count-buttons
-      = render 'projects/buttons/star'
-      = render 'projects/buttons/fork'
-
-    .clone-row
-      .project-clone-holder
-        = render "shared/clone_panel"
-
-      .split-repo-buttons
-        .btn-group.pull-left
+  %div{ class: (container_class) }
+    .row
+      .project-image-container
+        = project_icon(@project, alt: '', class: 'project-avatar avatar s70')
+      .project-info
+        .cover-title.project-home-desc
+          %h1
+            = @project.name
+            %span.visibility-icon.has-tooltip{data: { container: 'body' }, title: visibility_icon_description(@project)}
+              = visibility_level_icon(@project.visibility_level, fw: false)
+
+        - if @project.description.present?
+          .cover-desc.project-home-desc
+            = markdown(@project.description, pipeline: :description)
+
+        - if forked_from_project = @project.forked_from_project
+          .cover-desc
+            Forked from
+            = link_to project_path(forked_from_project) do
+              = forked_from_project.namespace.try(:name)
+
+        - if @project.mirror?
+          - import_url = @project.safe_import_url
+          %p
+            Mirrored from #{link_to import_url, import_url}.
+
+            = render "shared/mirror_status"
+
+        .project-repo-buttons
+          .count-buttons
+            = render 'projects/buttons/star'
+            = render 'projects/buttons/fork'
+
+          .project-clone-holder
+            = render "shared/clone_panel"
+
+      .project-repo-buttons.project-right-buttons
+        - if current_user
+          = render 'shared/members/access_request_buttons', source: @project
+        .btn-group
           = render "projects/buttons/update_mirror"
           = 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/_issues_settings.html.haml b/app/views/projects/_issues_settings.html.haml
index 083e9ae845a79ade5df41473644baaacd187c227..51c38a0288904978d4ff894f4a0a8b88a5d74024 100644
--- a/app/views/projects/_issues_settings.html.haml
+++ b/app/views/projects/_issues_settings.html.haml
@@ -1,11 +1,7 @@
-%fieldset.issues-feature
-  %legend
-    Issues:
-
+%fieldset.features.append-bottom-0.issues-feature
+  %h5.prepend-top-0
+    Issues
   .form-group
-    = f.label :issues_template, class: 'control-label' do
+    = f.label :issues_template, class: 'label-light' do
       Description template
-    .col-sm-10
-      = f.text_area :issues_template, class: "form-control", rows: 3
-
-
+    = f.text_area :issues_template, class: "form-control", rows: 3
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
index 81afea2c60a214bc825965fe146f4ccc0a31c49b..28a28282fd3b3c51abaeb9c776ee39be6e4f2ad3 100644
--- a/app/views/projects/_md_preview.html.haml
+++ b/app/views/projects/_md_preview.html.haml
@@ -7,6 +7,12 @@
       %li
         %a.js-md-preview-button{ href: "#md-preview-holder", tabindex: -1 }
           Preview
+
+      - if defined?(@issue) && @issue.confidential?
+        %li.confidential-issue-warning
+          = icon('warning')
+          %span This is a confidential issue. Your comment will not be visible to the public.
+          
       %li.pull-right
         %button.zen-control.zen-control-full.js-zen-enter{ type: 'button', tabindex: -1 }
           Go full screen
diff --git a/app/views/projects/_merge_request_settings.html.haml b/app/views/projects/_merge_request_settings.html.haml
index accfc527eb582c3e39825e59b8cc85046b582fed..f8cec94d8ec4159fab0dbae8441a9549fea18e19 100644
--- a/app/views/projects/_merge_request_settings.html.haml
+++ b/app/views/projects/_merge_request_settings.html.haml
@@ -1,88 +1,91 @@
-%fieldset.merge-request-feature
-  %legend
-    Merge requests:
+%fieldset.merge-request-feature.append-bottom-default
+  %h5.prepend-top-0
+    Merge requests
 
   .form-group
-    = label_tag :merge_method_merge, class: 'control-label' do
+    = label_tag :merge_method_merge, class: 'label-light' do
       Merge method
-    .col-sm-10
-      .radio
-        = label_tag :project_merge_method_merge do
-          = f.radio_button :merge_method, :merge, class: "js-merge-method-radio"
-          %strong Merge commit
-          %br
-          %span.descr
-            A merge commit is created for every merge, and merging is allowed as long as there are no conflicts.
+    .radio
+      = label_tag :project_merge_method_merge do
+        = f.radio_button :merge_method, :merge, class: "js-merge-method-radio"
+        %strong Merge commit
+        %br
+        %span.descr
+          A merge commit is created for every merge, and merging is allowed as long as there are no conflicts.
 
-      .radio
-        = label_tag :project_merge_method_rebase_merge do
-          = f.radio_button :merge_method, :rebase_merge, class: "js-merge-method-radio"
-          %strong Merge commit with semi-linear history
-          %br
-          %span.descr
-            A merge commit is created for every merge, but merging is only allowed if the branch has been rebased.
-            This way you get a history that reads linearly (as with fast-forward merges), with the addition of merge commits.
-          %br
-          %span.descr
-            When the branch has not been rebased, the user is given the option to do so.
+    .radio
+      = label_tag :project_merge_method_rebase_merge do
+        = f.radio_button :merge_method, :rebase_merge, class: "js-merge-method-radio"
+        %strong Merge commit with semi-linear history
+        %br
+        %span.descr
+          A merge commit is created for every merge, but merging is only allowed if the branch has been rebased.
+          This way you get a history that reads linearly (as with fast-forward merges), with the addition of merge commits.
+        %br
+        %span.descr
+          When the branch has not been rebased, the user is given the option to do so.
 
-      .radio
-        = label_tag :project_merge_method_ff do
-          = f.radio_button :merge_method, :ff, class: "js-merge-method-radio"
-          %strong Fast-forward merge
-          %br
-          %span.descr
-            No merge commits are created and all merges are fast-forwarded, which means that merging is only allowed if the branch has been rebased.
-          %br
-          %span.descr
-            When the branch has not been rebased, the user is given the option to do so.
+    .radio
+      = label_tag :project_merge_method_ff do
+        = f.radio_button :merge_method, :ff, class: "js-merge-method-radio"
+        %strong Fast-forward merge
+        %br
+        %span.descr
+          No merge commits are created and all merges are fast-forwarded, which means that merging is only allowed if the branch has been rebased.
+        %br
+        %span.descr
+          When the branch has not been rebased, the user is given the option to do so.
 
   .form-group
-    = f.label :merge_requests_template, class: 'control-label' do
+    = f.label :merge_requests_template, class: 'label-light' do
       Description template
-    .col-sm-10
-      = f.text_area :merge_requests_template, class: "form-control", rows: 3
+    = f.text_area :merge_requests_template, class: "form-control", rows: 3
 
   .form-group
-    = f.label :approvals_before_merge, class: 'control-label' do
+    = f.label :approvals_before_merge, class: 'label-light' do
       Approvals required
-    .col-sm-10
-      = f.number_field :approvals_before_merge, class: "form-control", min: 0
-      .help-block
-        Number of users to approve a merge request before it can be accepted. 0 - approving is disabled
+    = f.number_field :approvals_before_merge, class: "form-control", min: 0
+    .help-block
+      Number of users to approve a merge request before it can be accepted. 0 - approving is disabled
 
   .form-group.reset-approvals-on-push
-    .col-sm-offset-2.col-sm-10
-      .checkbox
-        = f.label :reset_approvals_on_push do
-          = f.check_box :reset_approvals_on_push
-          %span.descr Reset approvals on push
-          .help-block Approvals are reset when new data is pushed to the merge request
+    .checkbox
+      = f.label :reset_approvals_on_push do
+        = f.check_box :reset_approvals_on_push
+        %span.descr Reset approvals on push
+        .help-block Approvals are reset when new data is pushed to the merge request
 
   .form-group
-    = f.label :approver_ids, class: 'control-label' do
+    = f.label :approver_ids, class: 'label-light' do
       Approvers
-    .col-sm-10
-      = users_select_tag("project[approver_ids]", multiple: true, class: 'input-large', scope: :all, email_user: true)
-      .help-block
-        Add an approver suggestion for each merge request
+    = users_select_tag("project[approver_ids]", multiple: true, class: 'input-large', scope: :all, email_user: true)
+    .help-block
+      Add an approver suggestion for each merge request
 
-      .panel.panel-default.prepend-top-10
-        .panel-heading
-          Approvers
-          %small
-            (#{@project.approvers.count(:all)})
-        %ul.well-list
-          - @project.approvers.each do |approver|
-            %li
-              = link_to approver.user.name, approver.user
-              .pull-right
-                = link_to namespace_project_approver_path(@project.namespace, @project, approver), data: { confirm: "Are you sure you want to remove approver #{approver.user.name}"}, method: :delete, class: "btn-xs btn btn-remove", title: 'Remove approver' do
-                  = icon("sign-out")
-                  Remove
-          - if @project.approvers.empty?
-            %li There are no approvers
+    .panel.panel-default.prepend-top-10
+      .panel-heading
+        Approvers
+        %small
+          (#{@project.approvers.count(:all)})
+      %ul.well-list
+        - @project.approvers.each do |approver|
+          %li
+            = link_to approver.user.name, approver.user
+            .pull-right
+              = link_to namespace_project_approver_path(@project.namespace, @project, approver), data: { confirm: "Are you sure you want to remove approver #{approver.user.name}"}, method: :delete, class: "btn-xs btn btn-remove", title: 'Remove approver' do
+                = icon("sign-out")
+                Remove
+        - if @project.approvers.empty?
+          %li There are no approvers
 
+  .form-group
+    .checkbox
+      = f.label :only_allow_merge_if_build_succeeds do
+        = f.check_box :only_allow_merge_if_build_succeeds
+        %strong Only allow merge requests to be merged if the build succeeds
+      .help-block
+        Builds need to be configured to enable this feature.
+        = link_to icon('question-circle'), help_page_path('workflow', 'merge_requests#only-allow-merge-requests-to-be-merged-if-the-build-succeeds')
 
 :javascript
   new UsersSelect();
diff --git a/app/views/projects/activity.html.haml b/app/views/projects/activity.html.haml
index 69fa4ad37c4bba65f5d3c93d38e9a1fce62a2e2f..3c0f01cbf6f683f5ce21358e80fcde39d3776d2d 100644
--- a/app/views/projects/activity.html.haml
+++ b/app/views/projects/activity.html.haml
@@ -1,5 +1,4 @@
 - page_title "Activity"
-- header_title project_title(@project, "Activity", activity_project_path(@project))
 
 = render 'projects/last_push'
 
diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml
index 49f95ff37dbe2b323fa1c69476ab3fd865d7eb53..539d07d634a030e9ddae6de4d4f92cfd505c6bda 100644
--- a/app/views/projects/artifacts/browse.html.haml
+++ b/app/views/projects/artifacts/browse.html.haml
@@ -1,5 +1,5 @@
 - page_title 'Artifacts', "#{@build.name} (##{@build.id})", 'Builds'
-= render 'projects/builds/header_title'
+- header_title project_title(@project, "Builds", project_builds_path(@project))
 
 .top-block.row-content-block.clearfix
   .pull-right
diff --git a/app/views/projects/badges/index.html.haml b/app/views/projects/badges/index.html.haml
index c22384ddf46c8b1052f7f9463a220474b4f50ca1..ee63bc55a303f622cfeaa633a9d1be94f005c56e 100644
--- a/app/views/projects/badges/index.html.haml
+++ b/app/views/projects/badges/index.html.haml
@@ -1,6 +1,5 @@
 - page_title 'Badges'
 - badges_path = namespace_project_badges_path(@project.namespace, @project)
-- header_title project_title(@project, 'Badges', badges_path)
 
 .prepend-top-10
   .panel.panel-default
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index 5f9a92ff93f6a1b1da496b94b4ec53ef519dfca5..377665b096f5885d620f2450d3089000db12e101 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -1,5 +1,4 @@
 - page_title "Blame", @blob.path, @ref
-- header_title project_title(@project, "Files", project_files_path(@project))
 
 %h3.page-title Blame view
 
diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml
index cdac50f7a8d3a5c7c14d57659c38b114ae8262e9..f8adf3278e865dfac15a8acf5ed13b8c57e83f17 100644
--- a/app/views/projects/blob/_actions.html.haml
+++ b/app/views/projects/blob/_actions.html.haml
@@ -16,6 +16,14 @@
 
 - if current_user
   .btn-group{ role: "group" }
+    = lock_file_link(html_options: {class: 'btn btn-sm path-lock'})
     = edit_blob_link
     = replace_blob_link
     = delete_blob_link
+
+- if license_allows_file_locks?
+  :javascript
+    PathLocks.init(
+      '#{toggle_namespace_project_path_locks_path(@project.namespace, @project)}',
+      '#{@path}'
+    );
diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml
index fefa652a3da9ccd9350e58bab8b24a065fbb1095..4071b59c0037961be8af50bc3451a121b0e3947e 100644
--- a/app/views/projects/blob/_editor.html.haml
+++ b/app/views/projects/blob/_editor.html.haml
@@ -16,6 +16,9 @@
       .license-selector.js-license-selector.hide
         = select_tag :license_type, grouped_options_for_select(licenses_for_select, @project.repository.license_key), include_blank: true, class: 'select2 license-select', data: {placeholder: 'Choose a license template', project: @project.name, fullname: @project.namespace.human_name}
 
+      .gitignore-selector.hidden
+        = dropdown_tag("Choose a .gitignore template", options: { toggle_class: 'js-gitignore-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { filenames: gitignore_names } } )
+
       .encoding-selector
         = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'select2'
 
diff --git a/app/views/projects/blob/_header_title.html.haml b/app/views/projects/blob/_header_title.html.haml
deleted file mode 100644
index 78c5ef20a5fa87f0b93f5f845ad94fd711085925..0000000000000000000000000000000000000000
--- a/app/views/projects/blob/_header_title.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-- header_title project_title(@project, "Files", project_files_path(@project))
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index effcce5a1c4b257c2be899697b1ae1cd413ac882..e4f04ca7764e8dd8b1320a1ce37120260ebd3957 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -1,5 +1,4 @@
 - page_title "Edit", @blob.path, @ref
-= render "header_title"
 
 .file-editor
   %ul.nav-links.no-bottom.js-edit-mode
diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml
index 0459699432e8b985f48d0460621543a1277897aa..c952bc7e5dbcf73de34502ab225769e10ffbd0d2 100644
--- a/app/views/projects/blob/new.html.haml
+++ b/app/views/projects/blob/new.html.haml
@@ -1,5 +1,4 @@
 - page_title "New File", @path.presence, @ref
-= render "header_title"
 
 %h3.page-title
   New File
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index 6988039b6c7233e30de71f7fc300fa7a949b18d0..ed670dae88d9f91ab9c4cbfc10d0a53a0080e923 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -1,5 +1,4 @@
 - page_title @blob.path, @ref
-= render "header_title"
 
 = render 'projects/last_push'
 
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index da546080760008accdf0574062b69b730e145655..33d23a804400521e206b80939cfe010b9a7f4fe6 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -27,12 +27,10 @@
     .controls.hidden-xs
       - if create_mr_button?(@repository.root_ref, branch.name)
         = link_to create_mr_path(@repository.root_ref, branch.name), class: 'btn btn-grouped btn-xs' do
-          = icon('plus')
           Merge Request
 
       - if branch.name != @repository.root_ref
         = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: branch.name), class: 'btn btn-grouped btn-xs', method: :post, title: "Compare" do
-          = icon("exchange")
           Compare
 
       - if can_remove_branch?(@project, branch.name)
diff --git a/app/views/projects/branches/destroy.js.haml b/app/views/projects/branches/destroy.js.haml
deleted file mode 100644
index a21ddaf4930291d203345287c86e13eca9fd855d..0000000000000000000000000000000000000000
--- a/app/views/projects/branches/destroy.js.haml
+++ /dev/null
@@ -1 +0,0 @@
-$('.js-totalbranch-count').html("#{@repository.branch_count}")
diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml
index 17de9144b8c93bea9c2d1d6168495ac0d7aadb7f..41529857a4790653cc0e78517181485971f29061 100644
--- a/app/views/projects/branches/index.html.haml
+++ b/app/views/projects/branches/index.html.haml
@@ -1,36 +1,37 @@
+- @no_container = true
 - page_title "Branches"
-= render "projects/commits/header_title"
 = render "projects/commits/head"
-.row-content-block
-  .pull-right
+
+%div{ class: (container_class) }
+  .top-area
+    .nav-text
+      Protected branches can be managed in project settings
+
     - if can? current_user, :push_code, @project
-      = link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do
-        = icon('plus')
-        New branch
-      &nbsp;
-    .dropdown.inline
-      %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
-        %span.light
-        - if @sort.present?
-          = @sort.humanize
-        - else
-          Name
-        %b.caret
-      %ul.dropdown-menu.dropdown-menu-align-right
-        %li
-          = link_to namespace_project_branches_path(sort: nil) do
-            Name
-          = link_to namespace_project_branches_path(sort: 'recently_updated') do
-            = sort_title_recently_updated
-          = link_to namespace_project_branches_path(sort: 'last_updated') do
-            = sort_title_oldest_updated
-  .oneline
-    Protected branches can be managed in project settings
+      .nav-controls
+        = link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do
+          New branch
+        .dropdown.inline
+          %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
+            %span.light
+            - if @sort.present?
+              = @sort.humanize
+            - else
+              Name
+            %b.caret
+          %ul.dropdown-menu.dropdown-menu-align-right
+            %li
+              = link_to namespace_project_branches_path(sort: nil) do
+                Name
+              = link_to namespace_project_branches_path(sort: 'recently_updated') do
+                = sort_title_recently_updated
+              = link_to namespace_project_branches_path(sort: 'last_updated') do
+                = sort_title_oldest_updated
 
-= render 'projects/commits/mirror_status'
+  = render 'projects/commits/mirror_status'
 
-- unless @branches.empty?
-  %ul.content-list.all-branches
-    - @branches.each do |branch|
-      = render "projects/branches/branch", branch: branch
-  = paginate @branches, theme: 'gitlab'
+  - unless @branches.empty?
+    %ul.content-list.all-branches
+      - @branches.each do |branch|
+        = render "projects/branches/branch", branch: branch
+    = paginate @branches, theme: 'gitlab'
diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml
index c659af6338c477dacc83c28417567c7064f20c0b..5a6c8c243fa77a36134a46eb145cebe9c60e37d0 100644
--- a/app/views/projects/branches/new.html.haml
+++ b/app/views/projects/branches/new.html.haml
@@ -1,5 +1,4 @@
 - page_title "New Branch"
-= render "projects/commits/header_title"
 
 - if @error
   .alert.alert-danger
diff --git a/app/views/projects/builds/_header.html.haml b/app/views/projects/builds/_header.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..51b5bd9db427d53a5c0c78d76de0edfbcb4370e0
--- /dev/null
+++ b/app/views/projects/builds/_header.html.haml
@@ -0,0 +1,16 @@
+.content-block.build-header
+  = ci_status_with_icon(@build.status)
+  Build
+  %strong ##{@build.id}
+  for commit
+  = link_to ci_status_path(@build.pipeline) do
+    %strong= @build.pipeline.short_sha
+  from
+  = link_to namespace_project_commits_path(@project.namespace, @project, @build.ref) do
+    %code
+      = @build.ref
+  - if @build.user
+    = render "user"
+  = time_ago_with_tooltip(@build.created_at)
+  %button.btn.btn-default.pull-right.visible-xs-block.visible-sm-block.build-gutter-toggle.js-sidebar-build-toggle{ role: "button", type: "button" }
+    = icon('angle-double-left')
diff --git a/app/views/projects/builds/_header_title.html.haml b/app/views/projects/builds/_header_title.html.haml
deleted file mode 100644
index 082dab1f5b06bc89483dd370ca709f1e1d9236e5..0000000000000000000000000000000000000000
--- a/app/views/projects/builds/_header_title.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-- header_title project_title(@project, "Builds", project_builds_path(@project))
diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..cab21f0cf19568bad1443c6cc5d64d5902cfd01e
--- /dev/null
+++ b/app/views/projects/builds/_sidebar.html.haml
@@ -0,0 +1,107 @@
+%aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar
+  .block.build-sidebar-header.visible-xs-block.visible-sm-block.append-bottom-default
+    Build
+    %strong ##{@build.id}
+    %a.gutter-toggle.pull-right.js-sidebar-build-toggle{ href: "#" }
+      = icon('angle-double-right')
+  - if @build.coverage
+    .block.block-first
+      .title
+        Test coverage
+      %p.build-detail-row
+        #{@build.coverage}%
+
+  - if can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?)
+    .block{ class: ("block-first" if !@build.coverage) }
+      .title
+        Build artifacts
+      - if @build.artifacts_expired?
+        %p.build-detail-row
+          The artifacts were removed
+          #{time_ago_with_tooltip(@build.artifacts_expire_at)}
+      - elsif @build.artifacts_expire_at
+        %p.build-detail-row
+          The artifacts will be removed in
+          %span.js-artifacts-remove= @build.artifacts_expire_at
+
+      - if @build.artifacts?
+        .btn-group.btn-group-justified{ role: :group }
+          - if @build.artifacts_expire_at
+            = link_to keep_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default', method: :post do
+              Keep
+
+          = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default' do
+            Download
+
+          - if @build.artifacts_metadata?
+            = link_to browse_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default' do
+              Browse
+
+  .block{ class: ("block-first" if !@build.coverage && !(can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?))) }
+    .title
+      Build details
+      - if @build.retryable?
+        = link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right', method: :post
+    - if @build.merge_request
+      %p.build-detail-row
+        %span.build-light-text Merge Request:
+        = link_to "#{@build.merge_request.to_reference}", merge_request_path(@build.merge_request)
+    - if @build.duration
+      %p.build-detail-row
+        %span.build-light-text Duration:
+        #{duration_in_words(@build.finished_at, @build.started_at)}
+    - if @build.finished_at
+      %p.build-detail-row
+        %span.build-light-text Finished:
+        #{time_ago_with_tooltip(@build.finished_at)}
+    - if @build.erased_at
+      %p.build-detail-row
+        %span.build-light-text Erased:
+        #{time_ago_with_tooltip(@build.erased_at)}
+    %p.build-detail-row
+      %span.build-light-text Runner:
+      - if @build.runner && current_user && current_user.admin
+        = link_to "##{@build.runner.id}", admin_runner_path(@build.runner.id)
+      - elsif @build.runner
+        \##{@build.runner.id}
+    .btn-group.btn-group-justified{ role: :group }
+      - if @build.has_trace?
+        = link_to 'Raw', raw_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default'
+      - if @build.active?
+        = link_to "Cancel", cancel_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default', method: :post
+      - if can?(current_user, :update_build, @project) && @build.erasable?
+        = link_to erase_namespace_project_build_path(@project.namespace, @project, @build),
+                  class: "btn btn-sm btn-default", method: :post,
+                  data: { confirm: "Are you sure you want to erase this build?" } do
+          Erase
+
+  - if @build.trigger_request
+    .build-widget
+      %h4.title
+        Trigger
+
+      %p
+        %span.build-light-text Token:
+        #{@build.trigger_request.trigger.short_token}
+
+      - if @build.trigger_request.variables
+        %p
+          %span.build-light-text Variables:
+
+        %code
+          - @build.trigger_request.variables.each do |key, value|
+            #{key}=#{value}
+
+  .block
+    .title
+      Commit message
+    %p.build-light-text.append-bottom-0
+      #{@build.pipeline.git_commit_message}
+
+  - if @build.tags.any?
+    .block
+      .title
+        Tags
+      - @build.tag_list.each do |tag|
+        %span.label.label-primary
+          = tag
diff --git a/app/views/projects/builds/_user.html.haml b/app/views/projects/builds/_user.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..2642de8021df5c5f59ccea327d5923889d4a1984
--- /dev/null
+++ b/app/views/projects/builds/_user.html.haml
@@ -0,0 +1,4 @@
+by
+%a{ href: user_path(@build.user) }
+  = image_tag avatar_icon(@build.user, 24), class: "avatar s24"
+  %strong= @build.user.to_reference
diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml
index 98f4a9416e5754a7faf5186e70da6c164b558fea..181547316aabcfe76b347344b4a5ae36a1dfecf0 100644
--- a/app/views/projects/builds/index.html.haml
+++ b/app/views/projects/builds/index.html.haml
@@ -1,65 +1,63 @@
+- @no_container = true
 - page_title "Builds"
-= render "header_title"
-
-.top-area
-  %ul.nav-links
-    %li{class: ('active' if @scope.nil?)}
-      = link_to project_builds_path(@project) do
-        All
-        %span.badge.js-totalbuilds-count
-          = number_with_delimiter(@all_builds.count(:id))
-
-
-    %li{class: ('active' if @scope == 'running')}
-      = link_to project_builds_path(@project, scope: :running) do
-        Running
-        %span.badge.js-running-count
-          = number_with_delimiter(@all_builds.running_or_pending.count(:id))
-
-    %li{class: ('active' if @scope == 'finished')}
-      = link_to project_builds_path(@project, scope: :finished) do
-        Finished
-        %span.badge.js-running-count
-          = number_with_delimiter(@all_builds.finished.count(:id))
-
-  .nav-controls
-    - if can?(current_user, :update_build, @project)
-      - 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
-
-      - unless @repository.gitlab_ci_yml
-        = link_to 'Get started with Builds', help_page_path('ci/quick_start', 'README'), class: 'btn btn-info'
-
-      = link_to ci_lint_path, class: 'btn btn-default' do
-        = icon('wrench')
-        %span CI Lint
-
-.row-content-block
-  #{(@scope || 'all').capitalize} builds from this project
-
-%ul.content-list
-  - if @builds.blank?
-    %li
-      .nothing-here-block No builds to show
-  - else
-    .table-holder
-      %table.table.builds
-        %thead
-          %tr
-            %th Status
-            %th Build ID
-            %th Commit
-            %th Ref
-            %th Stage
-            %th Name
-            %th Tags
-            %th Duration
-            %th Finished at
-            - if @project.build_coverage_enabled?
-              %th Coverage
-            %th
-
-        = render @builds, commit_sha: true, ref: true, stage: true, allow_retry: true, coverage: @project.build_coverage_enabled?
-
-    = paginate @builds, theme: 'gitlab'
+= render "projects/pipelines/head"
+
+%div{ class: (container_class) }
+  .top-area
+    %ul.nav-links
+      %li{class: ('active' if @scope.nil?)}
+        = link_to project_builds_path(@project) do
+          All
+          %span.badge.js-totalbuilds-count
+            = number_with_delimiter(@all_builds.count(:id))
+
+
+      %li{class: ('active' if @scope == 'running')}
+        = link_to project_builds_path(@project, scope: :running) do
+          Running
+          %span.badge.js-running-count
+            = number_with_delimiter(@all_builds.running_or_pending.count(:id))
+
+      %li{class: ('active' if @scope == 'finished')}
+        = link_to project_builds_path(@project, scope: :finished) do
+          Finished
+          %span.badge.js-running-count
+            = number_with_delimiter(@all_builds.finished.count(:id))
+
+    .nav-controls
+      - if can?(current_user, :update_build, @project)
+        - 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
+
+        - unless @repository.gitlab_ci_yml
+          = link_to 'Get started with Builds', help_page_path('ci/quick_start', 'README'), class: 'btn btn-info'
+
+        = link_to ci_lint_path, class: 'btn btn-default' do
+          %span CI Lint
+
+  %ul.content-list
+    - if @builds.blank?
+      %li
+        .nothing-here-block No builds to show
+    - else
+      .table-holder
+        %table.table.builds
+          %thead
+            %tr
+              %th Status
+              %th Build ID
+              %th Commit
+              %th Ref
+              %th Stage
+              %th Name
+              %th Tags
+              %th Duration
+              %th Finished at
+              - if @project.build_coverage_enabled?
+                %th Coverage
+              %th
+
+          = render @builds, commit_sha: true, ref: true, stage: true, allow_retry: true, coverage: @project.build_coverage_enabled?
+
+      = paginate @builds, theme: 'gitlab'
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index c7b9c36a3ab1c3cd8e118759602f786e4e496bd9..a26f8aeb315fbe127ff129f28a8c6a6d8e7b191c 100644
--- a/app/views/projects/builds/show.html.haml
+++ b/app/views/projects/builds/show.html.haml
@@ -1,20 +1,11 @@
 - page_title "#{@build.name} (##{@build.id})", "Builds"
-= render "header_title"
 - trace_with_state = @build.trace_with_state
+- header_title project_title(@project, "Builds", project_builds_path(@project))
 
 .build-page
-  .row-content-block.top-block
-    Build ##{@build.id} for commit
-    %strong.monospace= link_to @build.commit.short_sha, ci_status_path(@build.commit)
-    from
-    = link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref)
-    - merge_request = @build.merge_request
-    - if merge_request
-      via
-      = link_to "merge request #{merge_request.to_reference}", merge_request_path(merge_request)
+  = render "header"
 
-  #up-build-trace
-  - builds = @build.commit.builds.latest.to_a
+  - builds = @build.pipeline.builds.latest.to_a
   - if builds.size > 1
     %ul.nav-links.no-top.no-bottom
       - builds.each do |build|
@@ -34,18 +25,6 @@
             &middot;
             %i.fa.fa-warning
             This build was retried.
-
-  .row-content-block.middle-block
-    .build-head
-      .clearfix
-        = ci_status_with_icon(@build.status)
-        - if @build.duration
-          %span
-            %i.fa.fa-time
-            #{duration_in_words(@build.finished_at, @build.started_at)}
-        .pull-right
-          #{time_ago_with_tooltip(@build.finished_at) if @build.finished_at}
-
   - if @build.stuck?
     - unless @build.any_runners_online?
       .bs-callout.bs-callout-warning
@@ -65,158 +44,27 @@
           = link_to namespace_project_runners_path(@build.project.namespace, @build.project) do
             Runners page
 
-  .row.prepend-top-default
-    .col-md-9
-      .clearfix
-        - if @build.active?
-          .autoscroll-container
-            %button.btn.btn-success.btn-sm#autoscroll-button{:type => "button", :data => {:state => 'disabled'}} enable autoscroll
-          .clearfix
+  .prepend-top-default
+    - if @build.active?
+      .autoscroll-container
+        %button.btn.btn-success.btn-sm#autoscroll-button{:type => "button", :data => {:state => 'disabled'}} enable autoscroll
       #js-build-scroll.scroll-controls
-        = link_to '#up-build-trace', class: 'btn' do
+        = link_to '#build-trace', class: 'btn' do
           %i.fa.fa-angle-up
         = link_to '#down-build-trace', class: 'btn' do
           %i.fa.fa-angle-down
+    - if @build.erased?
+      .erased.alert.alert-warning
+        - erased_by = "by #{link_to @build.erased_by.name, user_path(@build.erased_by)}" if @build.erased_by
+        Build has been erased #{erased_by.html_safe} #{time_ago_with_tooltip(@build.erased_at)}
+    - else
+      %pre.build-trace#build-trace
+        %code.bash.js-build-output
+        = icon("refresh spin", class: "js-build-refresh")
 
-      - if @build.erased?
-        .erased.alert.alert-warning
-          - erased_by = "by #{link_to @build.erased_by.name, user_path(@build.erased_by)}" if @build.erased_by
-          Build has been erased #{erased_by.html_safe} #{time_ago_with_tooltip(@build.erased_at)}
-      - else
-        %pre.trace#build-trace
-          %code.bash
-            = preserve do
-              = raw trace_with_state[:html]
-              - if @build.active?
-                %i{:class => "fa fa-refresh fa-spin"}
-
-      %div#down-build-trace
-
-    .col-md-3
-      - if @build.coverage
-        .build-widget
-          %h4.title
-            Test coverage
-          %h1 #{@build.coverage}%
-
-      - if can?(current_user, :read_build, @project) && @build.artifacts?
-        .build-widget.artifacts
-          %h4.title Build artifacts
-          .center
-            .btn-group{ role: :group }
-              = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary' do
-                = icon('download')
-                Download
-
-              - if @build.artifacts_metadata?
-                = link_to browse_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary' do
-                  = icon('folder-open')
-                  Browse
-
-      .build-widget.build-controls
-        %h4.title
-          Build ##{@build.id}
-          - if can?(current_user, :update_build, @project)
-            .center
-              .btn-group{ role: :group }
-                - if @build.active?
-                  = link_to "Cancel", cancel_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-danger', method: :post
-                - elsif @build.retryable?
-                  = link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary', method: :post
-
-                - if @build.erasable?
-                  = link_to erase_namespace_project_build_path(@project.namespace, @project, @build),
-                            class: 'btn btn-sm btn-warning', method: :post,
-                            data: { confirm: 'Are you sure you want to erase this build?' } do
-                    = icon('eraser')
-                    Erase
-                - if @build.has_trace?
-                  = link_to 'Raw', raw_namespace_project_build_path(@project.namespace, @project, @build),
-                            class: 'btn btn-sm btn-success', target: '_blank'
-
-        .clearfix
-          - if @build.duration
-            %p
-              %span.attr-name Duration:
-              #{duration_in_words(@build.finished_at, @build.started_at)}
-          %p
-            %span.attr-name Created:
-            #{time_ago_with_tooltip(@build.created_at)}
-          - if @build.finished_at
-            %p
-              %span.attr-name Finished:
-              #{time_ago_with_tooltip(@build.finished_at)}
-          - if @build.erased_at
-            %p
-              %span.attr-name Erased:
-              #{time_ago_with_tooltip(@build.erased_at)}
-          %p
-            %span.attr-name Runner:
-            - if @build.runner && current_user && current_user.admin
-              = link_to "##{@build.runner.id}", admin_runner_path(@build.runner.id)
-            - elsif @build.runner
-              \##{@build.runner.id}
-
-      - if @build.trigger_request
-        .build-widget
-          %h4.title
-            Trigger
-
-          %p
-            %span.attr-name Token:
-            #{@build.trigger_request.trigger.short_token}
-
-          - if @build.trigger_request.variables
-            %p
-              %span.attr-name Variables:
-
-            %code
-              - @build.trigger_request.variables.each do |key, value|
-                #{key}=#{value}
-
-      .build-widget
-        %h4.title
-          Commit
-          .pull-right
-            %small
-              = link_to @build.commit.short_sha, ci_status_path(@build.commit), class: "monospace"
-        %p
-          %span.attr-name Branch:
-          = link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref)
-        %p
-          %span.attr-name Author:
-          #{@build.commit.git_author_name}
-        %p
-          %span.attr-name Message:
-          #{@build.commit.git_commit_message}
-
-      - if @build.tags.any?
-        .build-widget
-          %h4.title
-            Tags
-          - @build.tag_list.each do |tag|
-            %span.label.label-primary
-              = tag
-
-      - if @builds.present?
-        .build-widget
-          %h4.title #{pluralize(@builds.count(:id), "other build")} for
-          = succeed ":" do
-            = link_to @build.commit.short_sha, ci_status_path(@build.commit), class: "monospace"
-          %table.table.builds
-            - @builds.each_with_index do |build, i|
-              %tr.build
-                %td
-                  = ci_icon_for_status(build.status)
-                %td
-                  = link_to namespace_project_build_path(@project.namespace, @project, build) do
-                    - if build.name
-                      = build.name
-                    - else
-                      %span ##{build.id}
-
-                %td.status= build.status
+    #down-build-trace
 
+= render "sidebar"
 
-  :javascript
-    new CiBuild("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{@build.status}", "#{trace_with_state[:state]}")
+:javascript
+  new CiBuild("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{@build.status}", "#{trace_with_state[:state]}")
diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml
index 1e4c46fca2f159b7ef3e561ca4cd9206fe8afec0..16b8e1cca9134e9fab9867a7f579b31153f0a0e6 100644
--- a/app/views/projects/buttons/_dropdown.html.haml
+++ b/app/views/projects/buttons/_dropdown.html.haml
@@ -2,7 +2,7 @@
   .btn-group
     %a.btn.dropdown-toggle{href: '#', "data-toggle" => "dropdown"}
       = icon('plus')
-    %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown
+    %ul.dropdown-menu.dropdown-menu-align-right.project-home-dropdown
       - can_create_issue = can?(current_user, :create_issue, @project)
       - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
       - can_create_snippet = can?(current_user, :create_snippet, @project)
diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml
index 5fb5fe5af2fe007cb02ec7b9a971d14af4a404ce..34ad9fe2c438fe1617d56b1bb09f60fffa5402f2 100644
--- a/app/views/projects/buttons/_fork.html.haml
+++ b/app/views/projects/buttons/_fork.html.haml
@@ -12,7 +12,8 @@
       = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has-tooltip' do
         = icon('code-fork fw')
         Fork
-      = link_to namespace_project_forks_path(@project.namespace, @project), class: 'count-with-arrow' do
+      %div.count-with-arrow
         %span.arrow
         %span.count
-          = @project.forks_count
+          = link_to namespace_project_forks_path(@project.namespace, @project) do
+            = @project.forks_count
diff --git a/app/views/projects/buttons/_notifications.html.haml b/app/views/projects/buttons/_notifications.html.haml
index c1e3e5b73a2c3d945767969881aff32897765bfc..a7a97181096932d82026e4ae8feff5274405ad83 100644
--- a/app/views/projects/buttons/_notifications.html.haml
+++ b/app/views/projects/buttons/_notifications.html.haml
@@ -1,11 +1,11 @@
 - if @notification_setting
   = form_for @notification_setting, url: namespace_project_notification_setting_path(@project.namespace.becomes(Namespace), @project), method: :patch, remote: true, html: { class: 'inline', id: 'notification-form' } do |f|
     = f.hidden_field :level
-    %span.dropdown
-      %a.dropdown-new.btn.notifications-btn#notifications-button{href: '#', "data-toggle" => "dropdown"}
+    .dropdown.hidden-sm
+      %button.btn.btn-default.notifications-btn#notifications-button{ data: { toggle: "dropdown" }, aria: { haspopup: "true", expanded: "false" } }
         = icon('bell')
         = notification_title(@notification_setting.level)
-        = icon('angle-down')
-      %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown
+        = icon('caret-down')
+      %ul.dropdown-menu.dropdown-menu-no-wrap.dropdown-menu-align-right.dropdown-menu-selectable.dropdown-menu-large{ role: "menu" }
         - NotificationSetting.levels.each do |level|
           = notification_list_item(level.first, @notification_setting)
diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml
index 02dbb2985a4b664460cf8b49b5c973a61c46e0ea..71cf5582a4cd66668ca776511ea648054245b6b8 100644
--- a/app/views/projects/buttons/_star.html.haml
+++ b/app/views/projects/buttons/_star.html.haml
@@ -1,5 +1,5 @@
 - if current_user
-  = link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star has-tooltip', method: :post, remote: true, title: "Star project" do
+  = link_to toggle_star_namespace_project_path(@project.namespace, @project), { class: 'btn star-btn toggle-star has-tooltip', method: :post, remote: true, title: current_user.starred?(@project) ? 'Unstar project' : 'Star project' } do
     - if current_user.starred?(@project)
       = icon('star fw')
       %span.starred Unstar
diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml
index 962b9fb2595f7156e6cc1f1aa99872531e9acf53..5bd6e3f0ebc67ad4c8042d3afe183611e9daf5da 100644
--- a/app/views/projects/ci/builds/_build.html.haml
+++ b/app/views/projects/ci/builds/_build.html.haml
@@ -42,29 +42,25 @@
   %td
     = build.name
 
-    .pull-right
-      .label-container
-        - if build.tags.any?
-          - build.tags.each do |tag|
-            %span.label.label-primary
-              = tag
-        - if build.try(:trigger_request)
-          %span.label.label-info triggered
-        - if build.try(:allow_failure)
-          %span.label.label-danger allowed to fail
-        - if defined?(retried) && retried
-          %span.label.label-warning retried
+  %td
+    .label-container
+      - if build.tags.any?
+        - build.tags.each do |tag|
+          %span.label.label-primary
+            = tag
+      - if build.try(:trigger_request)
+        %span.label.label-info triggered
+      - if build.try(:allow_failure)
+        %span.label.label-danger allowed to fail
+      - if defined?(retried) && retried
+        %span.label.label-warning retried
 
   %td.duration
     - if build.duration
-      = icon("clock-o")
-      &nbsp;
       #{duration_in_words(build.finished_at, build.started_at)}
 
   %td.timestamp
     - if build.finished_at
-      = icon("calendar")
-      &nbsp;
       %span #{time_ago_with_tooltip(build.finished_at)}
 
   - if defined?(coverage) && coverage
diff --git a/app/views/projects/ci/commits/_commit.html.haml b/app/views/projects/ci/commits/_commit.html.haml
deleted file mode 100644
index 13162b41f9b514865f874cbdc20141125d9d895b..0000000000000000000000000000000000000000
--- a/app/views/projects/ci/commits/_commit.html.haml
+++ /dev/null
@@ -1,77 +0,0 @@
-- status = commit.status
-%tr.commit
-  %td.commit-link
-    = link_to namespace_project_pipeline_path(@project.namespace, @project, commit.id), class: "ci-status ci-#{status}" do
-      = ci_icon_for_status(status)
-      %strong ##{commit.id}
-
-  %td
-    %div.branch-commit
-      - if commit.ref
-        = link_to commit.ref, namespace_project_commits_path(@project.namespace, @project, commit.ref), class: "monospace"
-        &middot;
-      = link_to commit.short_sha, namespace_project_commit_path(@project.namespace, @project, commit.sha), class: "commit-id monospace"
-      &nbsp;
-      - if commit.latest?
-        %span.label.label-success latest
-      - if commit.tag?
-        %span.label.label-primary tag
-      - if commit.triggered?
-        %span.label.label-primary triggered
-      - if commit.yaml_errors.present?
-        %span.label.label-danger.has-tooltip{ title: "#{commit.yaml_errors}" } yaml invalid
-      - if commit.builds.any?(&:stuck?)
-        %span.label.label-warning stuck
-
-      %p
-        %span
-          - if commit_data = commit.commit_data
-            = link_to_gfm commit_data.title, namespace_project_commit_path(@project.namespace, @project, commit_data.id), class: "commit-row-message"
-          - else
-            Cant find HEAD commit for this branch
-
-
-    - stages_status = commit.statuses.stages_status
-    - stages.each do |stage|
-      %td
-        - if status = stages_status[stage]
-          - tooltip = "#{stage.titleize}: #{status}"
-          %span.has-tooltip{ title: "#{tooltip}", class: "ci-status-icon-#{status}" }
-            = ci_icon_for_status(status)
-
-  %td
-    - if commit.started_at && commit.finished_at
-      %p
-        = icon("clock-o")
-        &nbsp;
-        #{duration_in_words(commit.finished_at, commit.started_at)}
-    - if commit.finished_at
-      %p
-        = icon("calendar")
-        &nbsp;
-        #{time_ago_with_tooltip(commit.finished_at)}
-
-  %td
-    .controls.hidden-xs.pull-right
-      - artifacts = commit.builds.latest.select { |b| b.artifacts? }
-      - if artifacts.present?
-        .dropdown.inline.build-artifacts
-          %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
-            = icon('download')
-            %b.caret
-          %ul.dropdown-menu.dropdown-menu-align-right
-            - artifacts.each do |build|
-              %li
-                = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, build), rel: 'nofollow' do
-                  = icon("download")
-                  %span #{build.name}
-
-      - if can?(current_user, :update_pipeline, @project)
-        &nbsp;
-        - if commit.retryable? && commit.builds.failed.any?
-          = link_to retry_namespace_project_pipeline_path(@project.namespace, @project, commit.id), class: 'btn has-tooltip', title: "Retry", method: :post do
-            = icon("repeat")
-        &nbsp;
-        - if commit.active?
-          = link_to cancel_namespace_project_pipeline_path(@project.namespace, @project, commit.id), class: 'btn btn-remove has-tooltip', title: "Cancel", method: :post do
-            = icon("remove")
diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..b8d8758fd2bb1eff19e349a301db2ff7b2b3b9e1
--- /dev/null
+++ b/app/views/projects/ci/pipelines/_pipeline.html.haml
@@ -0,0 +1,71 @@
+- status = pipeline.status
+%tr.commit
+  %td.commit-link
+    = link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: "ci-status ci-#{status}" do
+      = ci_icon_for_status(status)
+      %strong ##{pipeline.id}
+
+  %td
+    %div.branch-commit
+      - if pipeline.ref
+        = link_to pipeline.ref, namespace_project_commits_path(@project.namespace, @project, pipeline.ref), class: "monospace"
+        &middot;
+      = link_to pipeline.short_sha, namespace_project_commit_path(@project.namespace, @project, pipeline.sha), class: "commit-id monospace"
+      &nbsp;
+      - if pipeline.tag?
+        %span.label.label-primary tag
+      - elsif pipeline.latest?
+        %span.label.label-success.has-tooltip{ title: 'Latest build for this branch' } latest
+      - if pipeline.triggered?
+        %span.label.label-primary triggered
+      - if pipeline.yaml_errors.present?
+        %span.label.label-danger.has-tooltip{ title: "#{pipeline.yaml_errors}" } yaml invalid
+      - if pipeline.builds.any?(&:stuck?)
+        %span.label.label-warning stuck
+
+      %p.commit-title
+        - if commit_data = pipeline.commit_data
+          = link_to_gfm truncate(commit_data.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit_data.id), class: "commit-row-message"
+        - else
+          Cant find HEAD commit for this branch
+
+
+    - stages_status = pipeline.statuses.stages_status
+    - stages.each do |stage|
+      %td
+        - status = stages_status[stage]
+        - tooltip = "#{stage.titleize}: #{status || 'not found'}"
+        - if status
+          = link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline.id, anchor: stage), class: "has-tooltip ci-status-icon-#{status}", title: tooltip do
+            = ci_icon_for_status(status)
+        - else
+          .light.has-tooltip{ title: tooltip }
+            \-
+
+  %td
+    - if pipeline.started_at && pipeline.finished_at
+      %p.duration
+        #{duration_in_words(pipeline.finished_at, pipeline.started_at)}
+
+  %td
+    .controls.hidden-xs.pull-right
+      - artifacts = pipeline.builds.latest.select { |b| b.artifacts? }
+      - if artifacts.present?
+        .dropdown.inline.build-artifacts
+          %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
+            = icon('download')
+            %b.caret
+          %ul.dropdown-menu.dropdown-menu-align-right
+            - artifacts.each do |build|
+              %li
+                = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, build), rel: 'nofollow' do
+                  = icon("download")
+                  %span Download '#{build.name}' artifacts
+
+      - if can?(current_user, :update_pipeline, @project)
+        - if pipeline.retryable?
+          = link_to retry_namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: 'btn has-tooltip', title: "Retry", method: :post do
+            = icon("repeat")
+        - if pipeline.cancelable?
+          = link_to cancel_namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: 'btn btn-remove has-tooltip', title: "Cancel", method: :post do
+            = icon("remove")
diff --git a/app/views/projects/commit/_builds.html.haml b/app/views/projects/commit/_builds.html.haml
index 7f7a15aa2145ed7b2d4cd1ab6517ddddb3f81ec5..a508382578a7c9f24492317fb8412ff9771972f6 100644
--- a/app/views/projects/commit/_builds.html.haml
+++ b/app/views/projects/commit/_builds.html.haml
@@ -1,2 +1,2 @@
-- @ci_commits.each do |ci_commit|
-  = render "ci_commit", ci_commit: ci_commit, pipeline_details: true
+- @pipelines.each do |pipeline|
+  = render "pipeline", pipeline: pipeline, pipeline_details: true
diff --git a/app/views/projects/commit/_change.html.haml b/app/views/projects/commit/_change.html.haml
index 44ef1fdbbe32c210fd537f017a4fe087da6d6fa0..d9b800a4ded8da79719e96df1e13c80b804722bf 100644
--- a/app/views/projects/commit/_change.html.haml
+++ b/app/views/projects/commit/_change.html.haml
@@ -17,7 +17,7 @@
           .form-group.branch
             = label_tag 'target_branch', target_label, class: 'control-label'
             .col-sm-10
-              = select_tag "target_branch", grouped_options_refs, class: "select2 select2-sm js-target-branch"
+              = select_tag "target_branch", project_branches, class: "select2 select2-sm js-target-branch"
               - if can?(current_user, :push_code, @project)
                 .js-create-merge-request-container
                   .checkbox
diff --git a/app/views/projects/commit/_ci_commit.html.haml b/app/views/projects/commit/_ci_commit.html.haml
deleted file mode 100644
index 8228c067be071b5bb052436f59cb3d18058d5126..0000000000000000000000000000000000000000
--- a/app/views/projects/commit/_ci_commit.html.haml
+++ /dev/null
@@ -1,41 +0,0 @@
-.row-content-block.build-content.middle-block
-  .pull-right
-    - if can?(current_user, :update_pipeline, @project)
-      - if ci_commit.builds.latest.failed.any?(&:retryable?)
-        = link_to "Retry failed", retry_namespace_project_pipeline_path(@project.namespace, @project, ci_commit.id), class: 'btn btn-grouped btn-primary', method: :post
-
-      - if ci_commit.builds.running_or_pending.any?
-        = link_to "Cancel running", cancel_namespace_project_pipeline_path(@project.namespace, @project, ci_commit.id), data: { confirm: 'Are you sure?' }, class: 'btn btn-grouped btn-danger', method: :post
-
-  .oneline.clearfix
-    - if defined?(pipeline_details) && pipeline_details
-      Pipeline
-      = link_to "##{ci_commit.id}", namespace_project_pipeline_path(@project.namespace, @project, ci_commit.id), class: "monospace"
-      with
-      = pluralize ci_commit.statuses.count(:id), "build"
-      - if ci_commit.ref
-        for
-        = link_to ci_commit.ref, namespace_project_commits_path(@project.namespace, @project, ci_commit.ref), class: "monospace"
-      - if defined?(link_to_commit) && link_to_commit
-        for commit
-        = link_to ci_commit.short_sha, namespace_project_commit_path(@project.namespace, @project, ci_commit.sha), class: "monospace"
-      - if ci_commit.duration
-        in
-        = time_interval_in_words ci_commit.duration
-
-- if ci_commit.yaml_errors.present?
-  .bs-callout.bs-callout-danger
-    %h4 Found errors in your .gitlab-ci.yml:
-    %ul
-      - ci_commit.yaml_errors.split(",").each do |error|
-        %li= error
-    You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path}
-
-- if @project.builds_enabled? && !ci_commit.ci_yaml_file
-  .bs-callout.bs-callout-warning
-    \.gitlab-ci.yml not found in this commit
-
-.table-holder
-  %table.table.builds
-    - ci_commit.statuses.stages.each do |stage|
-      = render 'projects/commit/ci_stage', stage: stage, statuses: ci_commit.statuses.where(stage: stage)
diff --git a/app/views/projects/commit/_ci_stage.html.haml b/app/views/projects/commit/_ci_stage.html.haml
index aaa318e1eb3f0c947ace8b1ecd63abaab59e3d4d..ae7bb01223eda241fe5b031fca23dc11bda5ee0a 100644
--- a/app/views/projects/commit/_ci_stage.html.haml
+++ b/app/views/projects/commit/_ci_stage.html.haml
@@ -1,6 +1,7 @@
 %tr
   %th{colspan: 10}
     %strong
+      %a{name: stage}
       - status = statuses.latest.status
       %span{class: "ci-status-link ci-status-icon-#{status}"}
         = ci_icon_for_status(status)
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 028564c93052843c583841f8a8e9f9bfa32e02e9..b117517c0ddeb4133b3a7fead57ed3c39f3f9c62 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -1,32 +1,35 @@
-.pull-right.commit-action-buttons
-  %div
+.commit-info-row.commit-info-row-header
+  %span.hidden-xs Authored by
+  %strong
+    = commit_author_link(@commit, avatar: true, size: 24)
+  #{time_ago_with_tooltip(@commit.authored_date)}
+
+  .pull-right.commit-action-buttons
     - if defined?(@notes_count) && @notes_count > 0
-      %span.btn.disabled.btn-grouped
-        %i.fa.fa-comment
+      %span.btn.disabled.btn-grouped.hidden-xs
+        = icon('comment')
         = @notes_count
-    .pull-left.btn-group
-      %a.btn.btn-grouped.dropdown-toggle{ data: {toggle: :dropdown} }
-        %i.fa.fa-download
-        Download as
-        %span.caret
-      %ul.dropdown-menu
+    = link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-grouped hidden-xs hidden-sm" do
+      Browse Files
+    .dropdown.inline
+      %a.btn.btn-default.dropdown-toggle{ data: { toggle: "dropdown" } }
+        %span.hidden-xs Options
+        %span.caret.commit-options-dropdown-caret
+      %ul.dropdown-menu.dropdown-menu-align-right
+        %li.visible-xs-block.visible-sm-block
+          = link_to namespace_project_tree_path(@project.namespace, @project, @commit) do
+            Browse Files
+        - unless @commit.has_been_reverted?(current_user)
+          %li.clearfix
+            = revert_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id), has_tooltip: false)
+        %li.clearfix
+          = cherry_pick_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id), has_tooltip: false)
+        %li.divider
+        %li.dropdown-header
+          Download
         - unless @commit.parents.length > 1
           %li= link_to "Email Patches", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch)
         %li= link_to "Plain Diff",    namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff)
-    = link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-grouped" do
-      = icon('files-o')
-      Browse Files
-    - unless @commit.has_been_reverted?(current_user)
-      = revert_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id))
-    = cherry_pick_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id))
-  %div
-
-%p
-.commit-info-row
-  %span.light Authored by
-  %strong
-    = commit_author_link(@commit, avatar: true, size: 24)
-  #{time_ago_with_tooltip(@commit.authored_date)}
 
 - if @commit.different_committer?
   .commit-info-row
@@ -36,8 +39,9 @@
     #{time_ago_with_tooltip(@commit.committed_date)}
 
 .commit-info-row
-  %span.light Commit
-  = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace"
+  %span.hidden-xs.hidden-sm Commit
+  = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace hidden-xs hidden-sm"
+  = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace visible-xs-inline visible-sm-inline"
   = clipboard_button(clipboard_text: @commit.id)
   %span.cgray= pluralize(@commit.parents.count, "parent")
   - @commit.parents.each do |parent|
@@ -49,20 +53,20 @@
 - if @commit.status
   .commit-info-row
     Builds for
-    = pluralize(@commit.ci_commits.count, 'pipeline')
+    = pluralize(@commit.pipelines.count, 'pipeline')
     = link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id), class: "ci-status-link ci-status-icon-#{@commit.status}" do
       = ci_icon_for_status(@commit.status)
       = ci_label_for_status(@commit.status)
-    - if @commit.ci_commits.duration
+    - if @commit.pipelines.duration
       in
-      = time_interval_in_words @commit.ci_commits.duration
+      = time_interval_in_words @commit.pipelines.duration
 
 .commit-box.content-block
   %h3.commit-title
-    = markdown escape_once(@commit.title), pipeline: :single_line
+    = markdown escape_once(@commit.title), pipeline: :single_line, author: @commit.author
   - if @commit.description.present?
     %pre.commit-description
-      = preserve(markdown(escape_once(@commit.description), pipeline: :single_line))
+      = preserve(markdown(escape_once(@commit.description), pipeline: :single_line, author: @commit.author))
 
 :javascript
   $(".commit-info.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}");
diff --git a/app/views/projects/commit/_pipeline.html.haml b/app/views/projects/commit/_pipeline.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..0411137b7c61bea020bda1b72508087e662b91e4
--- /dev/null
+++ b/app/views/projects/commit/_pipeline.html.haml
@@ -0,0 +1,52 @@
+.row-content-block.build-content.middle-block
+  .pull-right
+    - if can?(current_user, :update_pipeline, pipeline.project)
+      - if pipeline.builds.latest.failed.any?(&:retryable?)
+        = link_to "Retry failed", retry_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: 'btn btn-grouped btn-primary', method: :post
+
+      - if pipeline.builds.running_or_pending.any?
+        = link_to "Cancel running", cancel_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), data: { confirm: 'Are you sure?' }, class: 'btn btn-grouped btn-danger', method: :post
+
+  .oneline.clearfix
+    - if defined?(pipeline_details) && pipeline_details
+      Pipeline
+      = link_to "##{pipeline.id}", namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: "monospace"
+      with
+      = pluralize pipeline.statuses.count(:id), "build"
+      - if pipeline.ref
+        for
+        = link_to pipeline.ref, namespace_project_commits_path(pipeline.project.namespace, pipeline.project, pipeline.ref), class: "monospace"
+      - if defined?(link_to_commit) && link_to_commit
+        for commit
+        = link_to pipeline.short_sha, namespace_project_commit_path(pipeline.project.namespace, pipeline.project, pipeline.sha), class: "monospace"
+      - if pipeline.duration
+        in
+        = time_interval_in_words pipeline.duration
+
+- if pipeline.yaml_errors.present?
+  .bs-callout.bs-callout-danger
+    %h4 Found errors in your .gitlab-ci.yml:
+    %ul
+      - pipeline.yaml_errors.split(",").each do |error|
+        %li= error
+    You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path}
+
+- if pipeline.project.builds_enabled? && !pipeline.ci_yaml_file
+  .bs-callout.bs-callout-warning
+    \.gitlab-ci.yml not found in this commit
+
+.table-holder
+  %table.table.builds
+    %thead
+      %tr
+        %th Status
+        %th Build ID
+        %th Name
+        %th Tags
+        %th Duration
+        %th Finished at
+        - if pipeline.project.build_coverage_enabled?
+          %th Coverage
+        %th
+    - pipeline.statuses.stages.each do |stage|
+      = render 'projects/commit/ci_stage', stage: stage, statuses: pipeline.statuses.where(stage: stage)
diff --git a/app/views/projects/commit/builds.html.haml b/app/views/projects/commit/builds.html.haml
index 7118a4846c6744fa4ecdb69d1e6bca3c6e4e6e2b..2f051fb90e076e3deac6de236963f0e502b4dcf0 100644
--- a/app/views/projects/commit/builds.html.haml
+++ b/app/views/projects/commit/builds.html.haml
@@ -1,7 +1,7 @@
 - page_title "Builds", "#{@commit.title} (#{@commit.short_id})", "Commits"
-= render "projects/commits/header_title"
+
 .prepend-top-default
   = render "commit_box"
-= render "ci_menu"
 
+= render "ci_menu"
 = render "builds"
diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml
index e5e3d69603523c3797ede13e943c61f0cb286292..401cb4f7e30b6bb4c491afcb7a8edb469ed28789 100644
--- a/app/views/projects/commit/show.html.haml
+++ b/app/views/projects/commit/show.html.haml
@@ -1,8 +1,6 @@
 - page_title        "#{@commit.title} (#{@commit.short_id})", "Commits"
 - page_description  @commit.description
 
-= render "projects/commits/header_title"
-
 .prepend-top-default
   = render "commit_box"
 - if @commit.status
diff --git a/app/views/projects/commits/_commit.atom.builder b/app/views/projects/commits/_commit.atom.builder
new file mode 100644
index 0000000000000000000000000000000000000000..1657fb46163758fe8f9869e17e5079052f134e56
--- /dev/null
+++ b/app/views/projects/commits/_commit.atom.builder
@@ -0,0 +1,14 @@
+xml.entry do
+  xml.id      namespace_project_commit_url(@project.namespace, @project, id: commit.id)
+  xml.link    href: namespace_project_commit_url(@project.namespace, @project, id: commit.id)
+  xml.title   truncate(commit.title, length: 80)
+  xml.updated commit.committed_date.xmlschema
+  xml.media   :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(commit.author_email))
+
+  xml.author do |author|
+    xml.name commit.author_name
+    xml.email commit.author_email
+  end
+
+  xml.summary markdown(commit.description, pipeline: :single_line)
+end
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 655cb0ac3cb3eb51b8a4ec87cdf9593896df5afe..a959b34a5390d93aab2d47c4ca416fb477bf54b1 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -9,26 +9,30 @@
 
 = cache(cache_key) do
   %li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" }
+    = commit_author_avatar(commit, size: 36)
     .commit-row-title
       %span.item-title
         = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message"
+        %span.commit-row-message.visible-xs-inline
+          &middot;
+          = commit.short_id
+        - if commit.status
+          = render_commit_status(commit, cssclass: 'visible-xs-inline')
         - if commit.description?
-          %a.text-expander.js-toggle-button ...
+          %a.text-expander.hidden-xs.js-toggle-button ...
 
-      .pull-right
+      .commit-actions.hidden-xs
         - if commit.status
-          = render_commit_status(commit)
-        = clipboard_button(clipboard_text: commit.id)
-        = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
+          = render_commit_status(commit, cssclass: 'btn btn-transparent')
+        = clipboard_button_with_class({ clipboard_text: commit.id }, css_class: 'btn-transparent')
+        = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-short-id btn btn-transparent"
+        = link_to_browse_code(project, commit)
 
     - if commit.description?
-      .commit-row-description.js-toggle-content
-        %pre
-          = preserve(markdown(escape_once(commit.description), pipeline: :single_line))
+      %pre.commit-row-description.js-toggle-content
+        = preserve(markdown(escape_once(commit.description), pipeline: :single_line, author: commit.author))
 
     .commit-row-info
-      by
-      = commit_author_link(commit, avatar: true, size: 24)
-      .committed_ago
-        #{time_ago_with_tooltip(commit.committed_date)} &nbsp;
-      = link_to_browse_code(project, commit)
+      = commit_author_link(commit, avatar: false, size: 24)
+      authored
+      #{time_ago_with_tooltip(commit.committed_date)}
diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml
index 7283a78a64e4a42dda77044fa2a4e322d90aa9a7..dd12eae8f7e0a304e6b7dce5a998ff8bf4b864b5 100644
--- a/app/views/projects/commits/_commits.html.haml
+++ b/app/views/projects/commits/_commits.html.haml
@@ -4,18 +4,11 @@
 - commits, hidden = limited_commits(@commits)
 
 - commits.chunk { |c| c.committed_date.in_time_zone.to_date }.each do |day, commits|
-  .row.commits-row
-    .col-md-2.hidden-xs.hidden-sm
-      %h5.commits-row-date
-        %i.fa.fa-calendar
-        %span= day.strftime('%d %b, %Y')
-      .light
-        = pluralize(commits.count, 'commit')
-    .col-md-10.col-sm-12
-      %ul.content-list
-        = render commits, project: project
-  %hr.lists-separator
+  %li.commit-header= "#{day.strftime('%d %b, %Y')} #{pluralize(commits.count, 'commit')}"
+  %li.commits-row
+    %ul.list-unstyled.commit-list
+      = render commits, project: project
 
 - if hidden > 0
-  .alert.alert-warning
+  %li.alert.alert-warning
     #{number_with_delimiter(hidden)} additional commits have been omitted to prevent performance issues.
diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml
index d1bd76ab5290d593426c160fd4666bdd7fcf6073..b6fcb9c7cb7b8836a024d3faf80aeee3ce9423ec 100644
--- a/app/views/projects/commits/_head.html.haml
+++ b/app/views/projects/commits/_head.html.haml
@@ -1,24 +1,33 @@
-%ul.nav-links
-  = nav_link(controller: [:commit, :commits]) do
-    = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
-      Commits
-      %span.badge
-        = number_with_delimiter(@repository.commit_count)
+.scrolling-tabs-container
+  .nav-links.sub-nav.scrolling-tabs
+    %ul{ class: (container_class) }
+      .fade-left
+      = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
+        = link_to project_files_path(@project) do
+          Files
 
-  = nav_link(controller: %w(network)) do
-    = link_to namespace_project_network_path(@project.namespace, @project, current_ref) do
-      Network
+      = nav_link(controller: [:commit, :commits]) do
+        = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
+          Commits
 
-  = nav_link(controller: :compare) do
-    = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: current_ref) do
-      Compare
+      = nav_link(controller: %w(network)) do
+        = link_to namespace_project_network_path(@project.namespace, @project, current_ref) do
+          Network
 
-  = nav_link(html_options: {class: branches_tab_class}) do
-    = link_to namespace_project_branches_path(@project.namespace, @project) do
-      Branches
-      %span.badge.js-totalbranch-count= @repository.branch_count
+      = nav_link(controller: :compare) do
+        = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: current_ref) do
+          Compare
 
-  = nav_link(controller: [:tags, :releases]) do
-    = link_to namespace_project_tags_path(@project.namespace, @project) do
-      Tags
-      %span.badge.js-totaltags-count= @repository.tag_count
+      = nav_link(html_options: {class: branches_tab_class}) do
+        = link_to namespace_project_branches_path(@project.namespace, @project) do
+          Branches
+
+      = nav_link(controller: [:tags, :releases]) do
+        = link_to namespace_project_tags_path(@project.namespace, @project) do
+          Tags
+      - if license_allows_file_locks?
+        = nav_link(controller: [:path_locks]) do
+          = link_to namespace_project_path_locks_path(@project.namespace, @project) do
+            Locked Files
+
+      .fade-right
diff --git a/app/views/projects/commits/_header_title.html.haml b/app/views/projects/commits/_header_title.html.haml
deleted file mode 100644
index e4385893dd9d0ee5e912a960ef7dcb2f188b0931..0000000000000000000000000000000000000000
--- a/app/views/projects/commits/_header_title.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-- header_title project_title(@project, "Commits", project_commits_path(@project))
diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder
index e310fafd82ca029f0a64ef480316857a57ae2d18..30bb7412073922b35ea8ad2d6420abe8c01de6de 100644
--- a/app/views/projects/commits/show.atom.builder
+++ b/app/views/projects/commits/show.atom.builder
@@ -6,18 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
   xml.id      namespace_project_commits_url(@project.namespace, @project, @ref)
   xml.updated @commits.first.committed_date.xmlschema if @commits.any?
 
-  @commits.each do |commit|
-    xml.entry do
-      xml.id      namespace_project_commit_url(@project.namespace, @project, id: commit.id)
-      xml.link    href: namespace_project_commit_url(@project.namespace, @project, id: commit.id)
-      xml.title   truncate(commit.title, length: 80)
-      xml.updated commit.committed_date.xmlschema
-      xml.media   :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(commit.author_email))
-      xml.author do |author|
-        xml.name commit.author_name
-        xml.email commit.author_email
-      end
-      xml.summary markdown(commit.description, pipeline: :single_line)
-    end
-  end
+  xml << render(@commits) if @commits.any?
 end
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index c4fdd951dc8d577e6b841c6da0fc3ce59cb2218a..74c353f0c4a8b910b47a464057e9d9d9d197a208 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -1,44 +1,43 @@
+- @no_container = true
+
 - page_title "Commits", @ref
-= render "header_title"
 = content_for :meta_tags do
   - if current_user
     = 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 "head"
 
-.row-content-block.second-block
-  .tree-ref-holder
-    = render 'shared/ref_switcher', destination: 'commits'
-
-  .block-controls.hidden-xs.hidden-sm
-    - if @merge_request.present?
-      .control
-        = link_to "View Open Merge Request", namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn'
-    - elsif create_mr_button?(@repository.root_ref, @ref)
-      .control
-        = link_to create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' do
-          = icon('plus')
-          Create Merge Request
-
-    .control
-      = form_tag(namespace_project_commits_path(@project.namespace, @project, @id), method: :get, class: 'pull-left commits-search-form') do
-        = search_field_tag :search, params[:search], { placeholder: 'Filter by commit message', id: 'commits-search', class: 'form-control search-text-input', spellcheck: false }
+%div{ class: (container_class) }
+  .row-content-block.second-block.content-component-block
+    .tree-ref-holder
+      = render 'shared/ref_switcher', destination: 'commits'
+
+    .block-controls.hidden-xs.hidden-sm
+      - if @merge_request.present?
+        .control
+          = link_to "View Open Merge Request", namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn'
+      - elsif create_mr_button?(@repository.root_ref, @ref)
+        .control
+          = link_to create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' do
+            = icon('plus')
+            Create Merge Request
 
-    - if current_user && current_user.private_token
       .control
-        = link_to namespace_project_commits_path(@project.namespace, @project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Commits Feed", class: 'btn' do
-          = icon("rss")
-
-
-  %ul.breadcrumb.repo-breadcrumb
-    = commits_breadcrumbs
-
-= render 'projects/commits/mirror_status'
-
-%div{id: dom_id(@project)}
-  #commits-list.content_list= render "commits", project: @project
-.clear
-= spinner
+        = form_tag(namespace_project_commits_path(@project.namespace, @project, @id), method: :get, class: 'commits-search-form') do
+          = search_field_tag :search, params[:search], { placeholder: 'Filter by commit message', id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false }
+      - if current_user && current_user.private_token
+        .control
+          = link_to namespace_project_commits_path(@project.namespace, @project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Commits Feed", class: 'btn' do
+            = icon("rss")
+    %ul.breadcrumb.repo-breadcrumb
+      = commits_breadcrumbs
+
+  = render 'projects/commits/mirror_status'
+
+  %div{id: dom_id(@project)}
+    %ol#commits-list.list-unstyled.content_list
+      = render "commits", project: @project
+  = spinner
 
 :javascript
   CommitsList.init(#{@limit});
diff --git a/app/views/projects/compare/index.html.haml b/app/views/projects/compare/index.html.haml
index 5e188dd0f3ca5e0c39a92c3d12ca0b5c78e02242..c322942aeba0fb4ca75b1276d16036c6c3742942 100644
--- a/app/views/projects/compare/index.html.haml
+++ b/app/views/projects/compare/index.html.haml
@@ -1,17 +1,18 @@
+- @no_container = true
 - page_title "Compare"
-= render "projects/commits/header_title"
 = render "projects/commits/head"
 
-.row-content-block
-  Compare branches, tags or commit ranges.
-  %br
-  Fill input field with commit id like
-  %code.label-branch 4eedf23
-  or branch/tag name like
-  %code.label-branch master
-  and press compare button for the commits list and a code diff.
-  %br
-  Changes are shown <b>from</b> the version in the first field <b>to</b> the version in the second field.
+%div{ class: (container_class) }
+  .row-content-block.second-block.content-component-block
+    Compare branches, tags or commit ranges.
+    %br
+    Fill input field with commit id like
+    %code.label-branch 4eedf23
+    or branch/tag name like
+    %code.label-branch master
+    and press compare button for the commits list and a code diff.
+    %br
+    Changes are shown <b>from</b> the version in the first field <b>to</b> the version in the second field.
 
-.prepend-top-20
-  = render "form"
+  .prepend-top-20
+    = render "form"
diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml
index 625251682396de8bdffcbee035317ef7f993883c..cdc34f51d6df27fe96e6317f2a91baa2340d80c8 100644
--- a/app/views/projects/compare/show.html.haml
+++ b/app/views/projects/compare/show.html.haml
@@ -1,5 +1,4 @@
 - page_title "#{params[:from]}...#{params[:to]}"
-= render "projects/commits/header_title"
 = render "projects/commits/head"
 
 
diff --git a/app/views/projects/container_registry/_header_title.html.haml b/app/views/projects/container_registry/_header_title.html.haml
deleted file mode 100644
index f1863c52a3eabdb704ff265fd7650ab7e6a23953..0000000000000000000000000000000000000000
--- a/app/views/projects/container_registry/_header_title.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-- header_title project_title(@project, "Container Registry", project_container_registry_path(@project))
diff --git a/app/views/projects/container_registry/_tag.html.haml b/app/views/projects/container_registry/_tag.html.haml
index 4e9f936539b5642b5a0f1820081887b8684cb129..f35faa6afb5529716c06ad6e33bab17095644c40 100644
--- a/app/views/projects/container_registry/_tag.html.haml
+++ b/app/views/projects/container_registry/_tag.html.haml
@@ -9,11 +9,19 @@
     - else
       \-
   %td
-    = number_to_human_size(tag.total_size)
-    &middot;
-    = pluralize(tag.layers.size, "layer")
+    - if tag.total_size
+      = number_to_human_size(tag.total_size)
+      &middot;
+      = pluralize(tag.layers.size, "layer")
+    - else
+      .light
+        \-
   %td
-    = time_ago_in_words(tag.created_at)
+    - if tag.created_at
+      = time_ago_in_words(tag.created_at)
+    - else
+      .light
+        \-
   - if can?(current_user, :update_container_image, @project)
     %td.content
       .controls.hidden-xs.pull-right
diff --git a/app/views/projects/container_registry/index.html.haml b/app/views/projects/container_registry/index.html.haml
index e1e762410f28250abc9c8c010a594b549948486d..993da27310f4053f26eb2867b1bdf92660f6105d 100644
--- a/app/views/projects/container_registry/index.html.haml
+++ b/app/views/projects/container_registry/index.html.haml
@@ -1,5 +1,4 @@
 - page_title "Container Registry"
-= render "header_title"
 
 %hr
 
@@ -37,4 +36,4 @@
               %th
 
         - @tags.each do |tag|
-          = render 'tag', tag: tag
\ No newline at end of file
+          = render 'tag', tag: tag
diff --git a/app/views/projects/deployments/_commit.html.haml b/app/views/projects/deployments/_commit.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..0f9d9512d887b122d111ebfcee2ebdffbeadfa23
--- /dev/null
+++ b/app/views/projects/deployments/_commit.html.haml
@@ -0,0 +1,12 @@
+%div.branch-commit
+  - if deployment.ref
+    = link_to deployment.ref, namespace_project_commits_path(@project.namespace, @project, deployment.ref), class: "monospace"
+    &middot;
+  = link_to deployment.short_sha, namespace_project_commit_path(@project.namespace, @project, deployment.sha), class: "commit-id monospace"
+
+  %p.commit-title
+    %span
+      - if commit_title = deployment.commit_title
+        = link_to_gfm commit_title, namespace_project_commit_path(@project.namespace, @project, deployment.sha), class: "commit-row-message"
+      - else
+        Cant find HEAD commit for this branch
diff --git a/app/views/projects/deployments/_deployment.html.haml b/app/views/projects/deployments/_deployment.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..d08dd92f1f6baad77777ec22eb494b2fdc9d6b3f
--- /dev/null
+++ b/app/views/projects/deployments/_deployment.html.haml
@@ -0,0 +1,23 @@
+%tr.deployment
+  %td
+    %strong= "##{deployment.iid}"
+
+  %td
+    = render 'projects/deployments/commit', deployment: deployment
+
+  %td
+    - if deployment.deployable
+      = link_to namespace_project_build_path(@project.namespace, @project, deployment.deployable) do
+        = "#{deployment.deployable.name} (##{deployment.deployable.id})"
+
+  %td
+    #{time_ago_with_tooltip(deployment.created_at)}
+
+  %td
+    - if can?(current_user, :create_deployment, deployment) && deployment.deployable
+      .pull-right
+        = link_to retry_namespace_project_build_path(@project.namespace, @project, deployment.deployable), method: :post, class: 'btn btn-build' do
+          - if deployment.last?
+            Retry
+          - else
+            Rollback
diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml
index d9c4b410d32d712083a1458223221dfd36ccca4c..f18bc8c41b3df53fbc6bb81a87eb69fc13acaf43 100644
--- a/app/views/projects/diffs/_diffs.html.haml
+++ b/app/views/projects/diffs/_diffs.html.haml
@@ -11,6 +11,8 @@
         = commit_diff_whitespace_link(@project, @commit, class: 'hidden-xs')
       - elsif current_controller?(:merge_requests)
         = diff_merge_request_whitespace_link(@project, @merge_request, class: 'hidden-xs')
+      - elsif current_controller?(:compare)
+        = diff_compare_whitespace_link(@project, params[:from], params[:to], class: 'hidden-xs')
     .btn-group
       = inline_diff_btn
       = parallel_diff_btn
@@ -24,6 +26,7 @@
     - diff_commit = commit_for_diff(diff_file)
     - blob = project.repository.blob_for_diff(diff_commit, diff_file)
     - next unless blob
+    - blob.load_all_data!(project.repository) unless blob.only_display_raw?
 
     = render 'projects/diffs/file', i: index, project: project,
       diff_file: diff_file, diff_commit: diff_commit, blob: blob, diff_refs: diff_refs
diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml
index e5983c580392e5f0297a2d94a5f7b88105fee39e..2395ea3c275fc3171bb9f13f83fc0ab61b236474 100644
--- a/app/views/projects/diffs/_file.html.haml
+++ b/app/views/projects/diffs/_file.html.haml
@@ -49,6 +49,8 @@
         = render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i
       - else
         = render "projects/diffs/text_file", diff_file: diff_file, index: i
+    - elsif blob.only_display_raw?
+      .nothing-here-block This file is too large to display.
     - elsif blob.image?
       - old_file = project.repository.prev_blob_for_diff(diff_commit, diff_file)
       = render "projects/diffs/image", diff_file: diff_file, old_file: old_file, file: blob, index: i, diff_refs: diff_refs
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index fbb1770e12a36ff18188647562aae793ba211955..915202ad719d1d5b228d6139ac4687a21ccb7405 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -1,260 +1,223 @@
-.project-edit-container.prepend-top-default
-  .project-edit-errors
-  .project-edit-content
-    .panel.panel-default
-      .panel-heading
+.project-edit-container
+  .row.prepend-top-default
+    .col-lg-3.profile-settings-sidebar
+      %h4.prepend-top-0
         Project settings
-      .panel-body
-        = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit_project form-horizontal fieldset-form" }, authenticity_token: true do |f|
-
-          %fieldset
-            .form-group.project_name_holder
-              = f.label :name, class: 'control-label' do
-                Project name
-              .col-sm-10
-                = f.text_field :name, class: "form-control", id: "project_name_edit"
-
-
-            .form-group
-              = f.label :description, class: 'control-label' do
-                Project description
-                %span.light (optional)
-              .col-sm-10
-                = f.text_area :description, class: "form-control", rows: 3, maxlength: 250
-
-            - unless @project.empty_repo?
-              .form-group
-                = f.label :default_branch, "Default Branch", class: 'control-label'
-                .col-sm-10= f.select(:default_branch, @project.repository.branch_names, {}, {class: 'select2 select-wide'})
-
-
-          = render 'shared/visibility_level', f: f, visibility_level: @project.visibility_level, can_change_visibility_level: can_change_visibility_level?(@project, current_user), form_model: @project
-
+    .col-lg-9
+      = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit-project" }, authenticity_token: true do |f|
+        %fieldset.append-bottom-0
           .form-group
-            = f.label :tag_list, "Tags", class: 'control-label'
-            .col-sm-10
-              = f.text_field :tag_list, value: @project.tag_list.to_s, maxlength: 2000, class: "form-control"
-              %p.help-block Separate tags with commas.
-
-          %fieldset.features
-            %legend
-              Features:
-            .form-group
-              .col-sm-offset-2.col-sm-10
-                .checkbox
-                  = f.label :issues_enabled do
-                    = f.check_box :issues_enabled
-                    %strong Issues
-                    %br
-                    %span.descr Lightweight issue tracking system for this project
-
-            .form-group
-              .col-sm-offset-2.col-sm-10
-                .checkbox
-                  = f.label :merge_requests_enabled do
-                    = f.check_box :merge_requests_enabled
-                    %strong Merge Requests
-                    %br
-                    %span.descr Submit changes to be merged upstream
-
-            .form-group
-              .col-sm-offset-2.col-sm-10
-                .checkbox
-                  = f.label :builds_enabled do
-                    = f.check_box :builds_enabled
-                    %strong Builds
-                    %br
-                    %span.descr Test and deploy your changes before merge
-
-            .form-group
-              .col-sm-offset-2.col-sm-10
-                .checkbox
-                  = f.label :wiki_enabled do
-                    = f.check_box :wiki_enabled
-                    %strong Wiki
-                    %br
-                    %span.descr Pages for project documentation
-
-            .form-group
-              .col-sm-offset-2.col-sm-10
-                .checkbox
-                  = f.label :snippets_enabled do
-                    = f.check_box :snippets_enabled
-                    %strong Snippets
-                    %br
-                    %span.descr Share code pastes with others out of git repository
-
-            - if Gitlab.config.registry.enabled
-              .form-group
-                .col-sm-offset-2.col-sm-10
-                  .checkbox
-                    = f.label :container_registry_enabled do
-                      = f.check_box :container_registry_enabled
-                      %strong Container Registry
-                      %br
-                      %span.descr Enable Container Registry for this repository
-
-          = render 'issues_settings', f: f
-          = render 'merge_request_settings', f: f
-          = render 'builds_settings', f: f
+            = f.label :name, class: 'label-light' do
+              Project name
+            = f.text_field :name, class: "form-control", id: "project_name_edit"
+          .form-group
+            = f.label :description, class: 'label-light' do
+              Project description
+              %span.light (optional)
+            = f.text_area :description, class: "form-control", rows: 3, maxlength: 250
 
-          %fieldset.features
-            %legend
-              Project avatar:
+          - unless @project.empty_repo?
             .form-group
-              .col-sm-offset-2.col-sm-10
-                - if @project.avatar?
-                  = project_icon("#{@project.namespace.to_param}/#{@project.to_param}", alt: '', class: 'avatar project-avatar s160')
-                %p.light
-                  - if @project.avatar_in_git
-                    Project avatar in repository: #{ @project.avatar_in_git }
-                %p.light
-                  - if @project.avatar?
-                    You can change your project avatar here
-                  - else
-                    You can upload a project avatar here
-                %a.choose-btn.btn.btn-sm.js-choose-project-avatar-button
-                  %i.icon-paper-clip
-                  %span Choose File ...
-                &nbsp;
-                %span.file_name.js-avatar-filename File name...
-                = f.file_field :avatar, class: "js-project-avatar-input hidden"
-                .light The maximum file size allowed is 200KB.
-                - if @project.avatar?
-                  %hr
-                  = link_to 'Remove avatar', namespace_project_avatar_path(@project.namespace, @project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar"
-
-          .form-actions
-            = f.submit 'Save changes', class: "btn btn-save"
-
-    .danger-settings
-      .panel.panel-default
-        .panel-heading Housekeeping
-        .errors-holder
-        .panel-body
-          %p
-            Runs a number of housekeeping tasks within the current repository,
-            such as compressing file revisions and removing unreachable objects.
-            %br
-
-          .form-actions
-            = link_to 'Housekeeping', housekeeping_namespace_project_path(@project.namespace, @project),
-                method: :post, class: "btn btn-default"
-
-      - if can? current_user, :archive_project, @project
-        - if @project.archived?
-          .panel.panel-success
-            .panel-heading
-              Unarchive project
-            .panel-body
-              %p
-                Unarchiving the project will mark its repository as active.
+              = f.label :default_branch, "Default Branch", class: 'label-light'
+              = f.select(:default_branch, @project.repository.branch_names, {}, {class: 'select2 select-wide'})
+        .form-group.project-visibility-level-holder
+          = f.label :visibility_level, class: 'label-light' do
+            Visibility Level
+            = link_to "(?)", help_page_path("public_access", "public_access")
+          - if can_change_visibility_level?(@project, current_user)
+            = render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: @project.visibility_level, form_model: @project)
+          - else
+            .info
+              = visibility_level_icon(@project.visibility_level)
+              %strong
+                = visibility_level_label(@project.visibility_level)
+              .light= visibility_level_description(@project.visibility_level, @project)
+        .form-group
+          = f.label :tag_list, "Tags", class: 'label-light'
+          = f.text_field :tag_list, value: @project.tag_list.to_s, maxlength: 2000, class: "form-control"
+          %p.help-block Separate tags with commas.
+        %hr
+        %fieldset.features.append-bottom-0
+          %h5.prepend-top-0
+            Features
+          .form-group
+            .checkbox
+              = f.label :issues_enabled do
+                = f.check_box :issues_enabled
+                %strong Issues
                 %br
-                The project can be committed to.
+                %span.descr Lightweight issue tracking system for this project
+          .form-group
+            .checkbox
+              = f.label :merge_requests_enabled do
+                = f.check_box :merge_requests_enabled
+                %strong Merge Requests
                 %br
-                %strong Once active this project shows up in the search and on the dashboard.
-
-              .form-actions
-                = link_to 'Unarchive project', unarchive_namespace_project_path(@project.namespace, @project),
-                    data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be committed to again." },
-                    method: :post, class: "btn btn-success"
-        - else
-          .panel.panel-warning
-            .panel-heading
-              Archive project
-            .panel-body
-              %p
-                Archiving the project will mark its repository as read-only.
+                %span.descr Submit changes to be merged upstream
+          .form-group
+            .checkbox
+              = f.label :builds_enabled do
+                = f.check_box :builds_enabled
+                %strong Builds
                 %br
-                It is hidden from the dashboard and doesn't show up in searches.
+                %span.descr Test and deploy your changes before merge
+          .form-group
+            .checkbox
+              = f.label :wiki_enabled do
+                = f.check_box :wiki_enabled
+                %strong Wiki
                 %br
-                %strong Archived projects cannot be committed to!
-
-              .form-actions
-                = link_to 'Archive project', archive_namespace_project_path(@project.namespace, @project),
-                    data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." },
-                    method: :post, class: "btn btn-warning"
-      - else
-        .nothing-here-block Only the project owner can archive a project
-
-      .panel.panel-default.panel.panel-warning
-        .panel-heading Rename repository
-        .errors-holder
-        .panel-body
-          = form_for([@project.namespace.becomes(Namespace), @project], html: { class: 'form-horizontal' }) do |f|
-            .form-group.project_name_holder
-              = f.label :name, class: 'control-label' do
-                Project name
-              .col-sm-9
-                .form-group
-                  = f.text_field :name, class: "form-control"
+                %span.descr Pages for project documentation
+          .form-group
+            .checkbox
+              = f.label :snippets_enabled do
+                = f.check_box :snippets_enabled
+                %strong Snippets
+                %br
+                %span.descr Share code pastes with others out of git repository
+          - if Gitlab.config.registry.enabled
             .form-group
-              = f.label :path, class: 'control-label' do
-                %span Path
-              .col-sm-9
-                .form-group
-                  .input-group
-                    .input-group-addon
-                      #{URI.join(root_url, @project.namespace.path)}/
-                    = f.text_field :path, class: 'form-control'
-                %ul
-                  %li Be careful. Renaming a project's repository can have unintended side effects.
-                  %li You will need to update your local repositories to point to the new location.
-            .form-actions
-              = f.submit 'Rename project', class: "btn btn-warning"
-
-      - if can?(current_user, :change_namespace, @project)
-        .panel.panel-default.panel.panel-danger
-          .panel-heading Transfer project
-          .errors-holder
-          .panel-body
-            = form_for([@project.namespace.becomes(Namespace), @project], url: transfer_namespace_project_path(@project.namespace, @project), method: :put, remote: true, html: { class: 'transfer-project form-horizontal' }) do |f|
-              .form-group
-                = label_tag :new_namespace_id, nil, class: 'control-label' do
-                  %span Namespace
-                .col-sm-9
-                  .form-group
-                    = select_tag :new_namespace_id, namespaces_options(@project.namespace_id), { prompt: 'Choose a project namespace', class: 'select2' }
-                  %ul
-                    %li Be careful. Changing the project's namespace can have unintended side effects.
-                    %li You can only transfer the project to namespaces you manage.
-                    %li You will need to update your local repositories to point to the new location.
-                    %li Project visibility level will be changed to match namespace rules when transfering to a group.
-              .form-actions
-                = f.submit 'Transfer project', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => transfer_project_message(@project) }
-      - else
-        .nothing-here-block Only the project owner can transfer a project
-
-      - if @project.forked?
-        - if can?(current_user, :remove_fork_project, @project)
-          = form_for([@project.namespace.becomes(Namespace), @project], url: remove_fork_namespace_project_path(@project.namespace, @project), method: :delete, remote: true, html: { class: 'transfer-project form-horizontal' }) do |f|
-            .panel.panel-default.panel.panel-danger
-              .panel-heading Remove fork relationship
-              .panel-body
-                %p
-                  This will remove the fork relationship to source project
-                  #{link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)}.
+              .checkbox
+                = f.label :container_registry_enabled do
+                  = f.check_box :container_registry_enabled
+                  %strong Container Registry
                   %br
-                  %strong Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source.
-                .form-actions
-                  = button_to 'Remove fork relationship', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_message(@project) }
+                  %span.descr Enable Container Registry for this repository
+        %hr
+        = render 'issues_settings', f: f
+        = render 'merge_request_settings', f: f
+        = render 'builds_settings', f: f
+        %hr
+        %fieldset.features.append-bottom-default
+          %h5.prepend-top-0
+            Project avatar
+          .form-group
+            - if @project.avatar?
+              = project_icon("#{@project.namespace.to_param}/#{@project.to_param}", alt: '', class: 'avatar project-avatar s160')
+            %p.light
+              - if @project.avatar_in_git
+                Project avatar in repository: #{ @project.avatar_in_git }
+            %a.choose-btn.btn.js-choose-project-avatar-button
+              Browse file...
+            %span.file_name.prepend-left-default.js-avatar-filename No file chosen
+            = f.file_field :avatar, class: "js-project-avatar-input hidden"
+            .help-block The maximum file size allowed is 200KB.
+            - if @project.avatar?
+              %hr
+              = link_to 'Remove avatar', namespace_project_avatar_path(@project.namespace, @project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar"
+        = f.submit 'Save changes', class: "btn btn-save"
+  .row.prepend-top-default
+  %hr
+  .row.prepend-top-default
+    .col-lg-3
+      %h4.prepend-top-0
+        Housekeeping
+      %p.append-bottom-0
+        %p
+          Runs a number of housekeeping tasks within the current repository,
+          such as compressing file revisions and removing unreachable objects.
+    .col-lg-9
+      = link_to 'Housekeeping', housekeeping_namespace_project_path(@project.namespace, @project),
+          method: :post, class: "btn btn-save"
+  %hr
+  - if can? current_user, :archive_project, @project
+    .row.prepend-top-default
+      .col-lg-3
+        %h4.warning-title.prepend-top-0
+          - if @project.archived?
+            Unarchive project
+          - else
+            Archive project
+        %p.append-bottom-0
+          - if @project.archived?
+            Unarchiving the project will mark its repository as active. The project can be committed to.
+          - else
+            Archiving the project will mark its repository as read-only. It is hidden from the dashboard and doesn't show up in searches.
+      .col-lg-9
+        - if @project.archived?
+          %p
+            %strong Once active this project shows up in the search and on the dashboard.
+          = link_to 'Unarchive project', unarchive_namespace_project_path(@project.namespace, @project),
+              data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be committed to again." },
+              method: :post, class: "btn btn-success"
         - else
-          .nothing-here-block Only the project owner can remove the fork relationship.
-
-      - if can?(current_user, :remove_project, @project)
-        .panel.panel-default.panel.panel-danger
-          .panel-heading Remove project
-          .panel-body
-            = form_tag(namespace_project_path(@project.namespace, @project), method: :delete, class: 'form-horizontal') do
-              %p
-                Removing the project will delete its repository and all related resources including issues, merge requests etc.
-                %br
-                %strong Removed projects cannot be restored!
-              .form-actions
-                = button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) }
-      - else
-        .nothing-here-block Only the project owner can remove a project.
-
+          %p
+            %strong Archived projects cannot be committed to!
+          = link_to 'Archive project', archive_namespace_project_path(@project.namespace, @project),
+              data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." },
+              method: :post, class: "btn btn-warning"
+  %hr
+  .row.prepend-top-default
+    .col-lg-3
+      %h4.prepend-top-0.warning-title
+        Rename repository
+    .col-lg-9
+      = form_for([@project.namespace.becomes(Namespace), @project]) do |f|
+        .form-group.project_name_holder
+          = f.label :name, class: 'label-light' do
+            Project name
+          .form-group
+            = f.text_field :name, class: "form-control"
+        .form-group
+          = f.label :path, class: 'label-light' do
+            %span Path
+          .form-group
+            .input-group
+              .input-group-addon
+                #{URI.join(root_url, @project.namespace.path)}/
+              = f.text_field :path, class: 'form-control'
+          %ul
+            %li Be careful. Renaming a project's repository can have unintended side effects.
+            %li You will need to update your local repositories to point to the new location.
+        = f.submit 'Rename project', class: "btn btn-warning"
+  - if can?(current_user, :change_namespace, @project)
+    %hr
+    .row.prepend-top-default
+      .col-lg-3
+        %h4.prepend-top-0.danger-title
+          Transfer project
+      .col-lg-9
+        = form_for([@project.namespace.becomes(Namespace), @project], url: transfer_namespace_project_path(@project.namespace, @project), method: :put, remote: true) do |f|
+          .form-group
+            = label_tag :new_namespace_id, nil, class: 'label-light' do
+              %span Namespace
+            .form-group
+              = select_tag :new_namespace_id, namespaces_options(@project.namespace_id), { prompt: 'Choose a project namespace', class: 'select2' }
+            %ul
+              %li Be careful. Changing the project's namespace can have unintended side effects.
+              %li You can only transfer the project to namespaces you manage.
+              %li You will need to update your local repositories to point to the new location.
+              %li Project visibility level will be changed to match namespace rules when transfering to a group.
+          = f.submit 'Transfer project', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => transfer_project_message(@project) }
+  - if @project.forked? && can?(current_user, :remove_fork_project, @project)
+    %hr
+    .row.prepend-top-default.append-bottom-default
+      .col-lg-3
+        %h4.prepend-top-0.danger-title
+          Remove fork relationship
+        %p.append-bottom-0
+          %p
+            This will remove the fork relationship to source project
+            = succeed "." do
+              = link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)
+      .col-lg-9
+        = form_for([@project.namespace.becomes(Namespace), @project], url: remove_fork_namespace_project_path(@project.namespace, @project), method: :delete, remote: true, html: { class: 'transfer-project' }) do |f|
+          %p
+            %strong Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source.
+          = button_to 'Remove fork relationship', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_message(@project) }
+  - if can?(current_user, :remove_project, @project)
+    %hr
+    .row.prepend-top-default.append-bottom-default
+      .col-lg-3
+        %h4.prepend-top-0.danger-title
+          Remove project
+        %p.append-bottom-0
+          Removing the project will delete its repository and all related resources including issues, merge requests etc.
+      .col-lg-9
+        = form_tag(namespace_project_path(@project.namespace, @project), method: :delete) do
+          %p
+            %strong Removed projects cannot be restored!
+          = button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) }
 
 .save-project-loader.hide
   .center
@@ -263,5 +226,4 @@
       Saving project.
     %p Please wait a moment, this page will automatically refresh when ready.
 
-
 = render 'shared/confirm_modal', phrase: @project.path
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index 1a2e59752fe5fab8cef7c1c6223f4728d26e0589..636beb73ec20fa2b3ca2118ddbb9ec73296e7994 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -15,10 +15,14 @@
       If you already have files you can push them using command line instructions below.
     %p
       Otherwise you can start with adding a
-      = link_to "README", new_readme_path, class: 'underlined-link'
+      = succeed ',' do
+        = link_to "README", new_readme_path, class: 'underlined-link'
+      a
+      = succeed ',' do
+        = link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE'), class: 'underlined-link'
       or a
-      = link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE'), class: 'underlined-link'
-      file to this project.
+      = link_to '.gitignore', add_special_file_path(@project, file_name: '.gitignore'), class: 'underlined-link'
+      to this project.
 
 - if can?(current_user, :push_code, @project)
   %div{ class: container_class }
diff --git a/app/views/projects/environments/_environment.html.haml b/app/views/projects/environments/_environment.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..eafa246d05fd7ddf0b95c0f3780426f65f000168
--- /dev/null
+++ b/app/views/projects/environments/_environment.html.haml
@@ -0,0 +1,17 @@
+- last_deployment = environment.last_deployment
+
+%tr.environment
+  %td
+    %strong
+      = link_to environment.name, namespace_project_environment_path(@project.namespace, @project, environment)
+
+  %td
+    - if last_deployment
+      = render 'projects/deployments/commit', deployment: last_deployment
+    - else
+      %p.commit-title
+        No deployments yet
+
+  %td
+    - if last_deployment
+      #{time_ago_with_tooltip(last_deployment.created_at)}
diff --git a/app/views/projects/environments/_form.html.haml b/app/views/projects/environments/_form.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..c07f4bd510ca81b928b7301f56e06818db80aac2
--- /dev/null
+++ b/app/views/projects/environments/_form.html.haml
@@ -0,0 +1,7 @@
+= form_for @environment, url: namespace_project_environments_path(@project.namespace, @project), html: { class: 'col-lg-9' } do |f|
+  = form_errors(@environment)
+  .form-group
+    = f.label :name, 'Name', class: 'label-light'
+    = f.text_field :name, required: true, class: 'form-control'
+  = f.submit 'Create environment', class: 'btn btn-create'
+  = link_to 'Cancel', namespace_project_environments_path(@project.namespace, @project), class: 'btn btn-cancel'
diff --git a/app/views/projects/environments/_header_title.html.haml b/app/views/projects/environments/_header_title.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..e056fccad5d9c865146b4950397b44738b0bddc2
--- /dev/null
+++ b/app/views/projects/environments/_header_title.html.haml
@@ -0,0 +1 @@
+- header_title project_title(@project, "Environments", project_environments_path(@project))
diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..ae9e77e7d89d211f5bef0a118ce506d23c48bc55
--- /dev/null
+++ b/app/views/projects/environments/index.html.haml
@@ -0,0 +1,23 @@
+- @no_container = true
+- page_title "Environments"
+= render "projects/pipelines/head"
+
+%div{ class: (container_class) }
+  - if can?(current_user, :create_environment, @project)
+    .top-area
+      .nav-controls
+        = link_to new_namespace_project_environment_path(@project.namespace, @project), class: 'btn btn-create' do
+          New environment
+
+  - if @environments.blank?
+    %ul.content-list.environments
+      %li.nothing-here-block
+        No environments to show
+  - else
+    .table-holder
+      %table.table.environments
+        %tbody
+          %th Environment
+          %th Last deployment
+          %th Date
+        = render @environments
diff --git a/app/views/projects/environments/new.html.haml b/app/views/projects/environments/new.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..54465828ba9efebafe63b8409ee8453fb66b143e
--- /dev/null
+++ b/app/views/projects/environments/new.html.haml
@@ -0,0 +1,9 @@
+- page_title 'New Environment'
+
+.row.prepend-top-default.append-bottom-default
+  .col-lg-3
+    %h4.prepend-top-0
+      New Environment
+    %p Environments allow you to track deployments of your application
+
+  = render 'form'
diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..069b77b5adf9d800aad2defd4729780e6378312a
--- /dev/null
+++ b/app/views/projects/environments/show.html.haml
@@ -0,0 +1,33 @@
+- @no_container = true
+- page_title "Environments"
+= render "projects/pipelines/head"
+
+%div{ class: (container_class) }
+  .top-area
+    .col-md-9
+      %h3.page-title= @environment.name.titleize
+
+    .col-md-3
+      .nav-controls
+        - if can?(current_user, :update_environment, @environment)
+          = link_to 'Destroy', namespace_project_environment_path(@project.namespace, @project, @environment), data: { confirm: 'Are you sure you want to delete this environment?' }, class: 'btn btn-danger', method: :delete
+
+  - if @deployments.blank?
+    %ul.content-list.environments
+      %li.nothing-here-block
+        No deployments for
+        %strong= @environment.name
+  - else
+    .table-holder
+      %table.table.environments
+        %thead
+          %tr
+            %th ID
+            %th Commit
+            %th Build
+            %th Date
+            %th
+
+        = render @deployments
+
+    = paginate @deployments, theme: 'gitlab'
diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml
index 1fe1d98bf13eaec3f612a947cb78bec96a6bb761..9322c82904f9cf7becbe6a58b26af19d0ed8f566 100644
--- a/app/views/projects/find_file/show.html.haml
+++ b/app/views/projects/find_file/show.html.haml
@@ -1,5 +1,4 @@
 - page_title "Find File", @ref
-- header_title project_title(@project, "Files", project_files_path(@project))
 
 .file-finder-holder.tree-holder.clearfix
   .nav-block
diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml
index 8129514964a3d0384084e9482f0865be4a3bbd6d..5bc5c71283ede18a5fa7d540a144b05ec7e58f86 100644
--- a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml
+++ b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml
@@ -40,24 +40,20 @@
   %td
     = generic_commit_status.name
 
-    .pull-right
-      - if generic_commit_status.tags.any?
-        - generic_commit_status.tags.each do |tag|
-          %span.label.label-primary
-            = tag
-      - if defined?(retried) && retried
-        %span.label.label-warning retried
+  %td
+    - if generic_commit_status.tags.any?
+      - generic_commit_status.tags.each do |tag|
+        %span.label.label-primary
+          = tag
+    - if defined?(retried) && retried
+      %span.label.label-warning retried
 
   %td.duration
     - if generic_commit_status.duration
-      = icon("clock-o")
-      &nbsp;
       #{duration_in_words(generic_commit_status.finished_at, generic_commit_status.started_at)}
 
   %td.timestamp
     - if generic_commit_status.finished_at
-      = icon("calendar")
-      &nbsp;
       %span #{time_ago_with_tooltip(generic_commit_status.finished_at)}
 
   - if defined?(coverage) && coverage
diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml
index 79a56647c536ba79a4836a05eeee991d972dea12..8becaea246f2912f008418bc3a60afd0a8a2c29a 100644
--- a/app/views/projects/graphs/_head.html.haml
+++ b/app/views/projects/graphs/_head.html.haml
@@ -1,3 +1,4 @@
+- page_specific_javascripts asset_path("graphs/application.js")
 %ul.nav-links
   = nav_link(action: :show) do
     = link_to 'Contributors', namespace_project_graph_path
diff --git a/app/views/projects/graphs/_header_title.html.haml b/app/views/projects/graphs/_header_title.html.haml
deleted file mode 100644
index 1e2f61cd22b53119c3b12d83f0a7d0f5f8f23830..0000000000000000000000000000000000000000
--- a/app/views/projects/graphs/_header_title.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-- header_title project_title(@project, "Graphs", namespace_project_graph_path(@project.namespace, @project, current_ref))
diff --git a/app/views/projects/graphs/ci.html.haml b/app/views/projects/graphs/ci.html.haml
index 9f05be9982b4324dd5e99410e6bf0806a993b4b0..19ccc125ea825f83b07583236c82880f9357b8d0 100644
--- a/app/views/projects/graphs/ci.html.haml
+++ b/app/views/projects/graphs/ci.html.haml
@@ -1,5 +1,4 @@
 - page_title "Continuous Integration", "Graphs"
-= render "header_title"
 = render 'head'
 .row-content-block.append-bottom-default
   .oneline
diff --git a/app/views/projects/graphs/ci/_overall.haml b/app/views/projects/graphs/ci/_overall.haml
index 4b12e5f2da1778773dd2a168a20159f9a39c36bd..edc4f7b079fc27457ad5865fae0e06f92ee0e253 100644
--- a/app/views/projects/graphs/ci/_overall.haml
+++ b/app/views/projects/graphs/ci/_overall.haml
@@ -16,4 +16,4 @@
   %li
     Commits covered:
     %strong
-      = @project.ci_commits.count(:all)
+      = @project.pipelines.count(:all)
diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml
index da9f648cc9c50430a507a9da1ba097ed2e16fabb..d9b2fb6c065d8f06bb64487b991205ec6557964c 100644
--- a/app/views/projects/graphs/commits.html.haml
+++ b/app/views/projects/graphs/commits.html.haml
@@ -1,5 +1,4 @@
 - page_title "Commits", "Graphs"
-= render "header_title"
 = render 'head'
 
 .row-content-block.append-bottom-default
diff --git a/app/views/projects/graphs/languages.html.haml b/app/views/projects/graphs/languages.html.haml
index ebecab1dbfcdf509133f8ce1767879fdb9572354..249c16f4709619ebc3d1c2c52200457330f6b877 100644
--- a/app/views/projects/graphs/languages.html.haml
+++ b/app/views/projects/graphs/languages.html.haml
@@ -1,5 +1,4 @@
 - page_title "Languages", "Graphs"
-= render "header_title"
 = render 'head'
 
 .row-content-block.append-bottom-default
diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml
index ad4a932d391fb8212fdc16688346266ac8a24855..33970e7b90912cee3848f9f6653de60441fe25ad 100644
--- a/app/views/projects/graphs/show.html.haml
+++ b/app/views/projects/graphs/show.html.haml
@@ -1,5 +1,4 @@
 - page_title "Contributors", "Graphs"
-= render "header_title"
 = render 'head'
 
 .row-content-block.append-bottom-default
@@ -19,7 +18,7 @@
   .header.clearfix
     %h3#date_header.page-title
     %p.light
-      Commits to #{@ref}, excluding merge commits. Limited by 6,000 commits
+      Commits to #{@ref}, excluding merge commits. Limited to 6,000 commits.
     %input#brush_change{:type => "hidden"}
   .graphs
     #contributors-master
diff --git a/app/views/projects/hooks/_project_hook.html.haml b/app/views/projects/hooks/_project_hook.html.haml
index 62eba5888a4f068c6f7ca090cd9376f5523e98c3..8151187d49961334912a881214bff77bfb32af86 100644
--- a/app/views/projects/hooks/_project_hook.html.haml
+++ b/app/views/projects/hooks/_project_hook.html.haml
@@ -3,7 +3,7 @@
     .col-md-8.col-lg-7
       %strong.light-header= hook.url
       %div
-        - %w(push_events tag_push_events issues_events note_events merge_requests_events build_events).each do |trigger|
+        - %w(push_events tag_push_events issues_events note_events merge_requests_events build_events wiki_page_events).each do |trigger|
           - if hook.send(trigger)
             %span.label.label-gray.deploy-project-label= trigger.titleize
     .col-md-4.col-lg-5.text-right-lg.prepend-top-5
diff --git a/app/views/projects/issues/_head.html.haml b/app/views/projects/issues/_head.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..403adb7426bacc3a40da31a80924baf72dfb0c38
--- /dev/null
+++ b/app/views/projects/issues/_head.html.haml
@@ -0,0 +1,25 @@
+.nav-links.sub-nav
+  %ul{ class: (container_class) }
+    - if project_nav_tab?(:issues) && !current_controller?(:merge_requests)
+      = nav_link(controller: :issues) do
+        = link_to url_for_project_issues(@project, only_path: true), title: 'Issues' do
+          %span
+            Issues
+
+    - if project_nav_tab?(:merge_requests) && current_controller?(:merge_requests)
+      = nav_link(controller: :merge_requests) do
+        = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests' do
+          %span
+            Merge Requests
+
+    - if project_nav_tab? :labels
+      = nav_link(controller: :labels) do
+        = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do
+          %span
+            Labels
+
+    - if project_nav_tab? :milestones
+      = nav_link(controller: :milestones) do
+        = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do
+          %span
+            Milestones
diff --git a/app/views/projects/issues/_header_title.html.haml b/app/views/projects/issues/_header_title.html.haml
deleted file mode 100644
index 99f03549c44aceb7394f3a7bb1e73a114ff2a8ab..0000000000000000000000000000000000000000
--- a/app/views/projects/issues/_header_title.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-- header_title project_title(@project, "Issues", namespace_project_issues_path(@project.namespace, @project))
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index dae800bd68e2ad2e83fc12f7dbca011fcd3b8ace..15404fdf70243739005a7bac9fa6a6e461a2156b 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -1,4 +1,4 @@
-%li{ id: dom_id(issue), class: issue_css_classes(issue), url: issue_path(issue) }
+%li{ id: dom_id(issue), class: issue_css_classes(issue), url: issue_path(issue), data: { labels: issue.label_ids, id: issue.id } }
   - if controller.controller_name == 'issues' && can?(current_user, :admin_issue, @project)
     .issue-check
       = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue"
@@ -6,7 +6,7 @@
   .issue-title.title
     %span.issue-title-text
       = confidential_icon(issue)
-      = link_to_gfm issue.title, issue_path(issue)
+      = link_to issue.title, issue_path(issue)
     %ul.controls
       - if issue.closed?
         %li
@@ -27,7 +27,7 @@
           = icon('thumbs-down')
           = downvotes
 
-      - note_count = issue.notes.user.nonawards.count
+      - note_count = issue.notes.user.count
       %li
         = link_to issue_path(issue, anchor: 'notes'), class: ('issue-no-comments' if note_count.zero?) do
           = icon('comments')
diff --git a/app/views/projects/issues/_merge_requests.html.haml b/app/views/projects/issues/_merge_requests.html.haml
index e953353567e16021038e5ae7469d1e47f85bffec..d80753718533e3738c05817d844417607c8ebfd2 100644
--- a/app/views/projects/issues/_merge_requests.html.haml
+++ b/app/views/projects/issues/_merge_requests.html.haml
@@ -2,12 +2,12 @@
   %h2.merge-requests-title
     = pluralize(@merge_requests.count, 'Related Merge Request')
   %ul.unstyled-list
-    - has_any_ci = @merge_requests.any?(&:ci_commit)
+    - has_any_ci = @merge_requests.any?(&:pipeline)
     - @merge_requests.each do |merge_request|
       %li
         %span.merge-request-ci-status
-          - if merge_request.ci_commit
-            = render_pipeline_status(merge_request.ci_commit)
+          - if merge_request.pipeline
+            = render_pipeline_status(merge_request.pipeline)
           - elsif has_any_ci
             = icon('blank fw')
         %span.merge-request-id
@@ -25,4 +25,5 @@
           - 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}
+      %li
+        = render partial: 'projects/issues/closed_by_box', locals: {merge_request_count: @merge_requests.count}
diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml
index 469429ccf3c41e67a0d79bb16532768dece8257c..e93b7e0d66d11103946754eedcefd34c9b33fd6e 100644
--- a/app/views/projects/issues/_new_branch.html.haml
+++ b/app/views/projects/issues/_new_branch.html.haml
@@ -1,13 +1,13 @@
 - if can?(current_user, :push_code, @project)
   .pull-right
     #new-branch{'data-path' => can_create_branch_namespace_project_issue_path(@project.namespace, @project, @issue)}
-      = link_to namespace_project_branches_path(@project.namespace, @project, branch_name: @issue.to_branch_name, issue_iid: @issue.iid), method: :post, class: 'btn has-tooltip', title: @issue.to_branch_name, disabled: 'disabled' do
+      = link_to namespace_project_branches_path(@project.namespace, @project, branch_name: @issue.to_branch_name, issue_iid: @issue.iid),
+        method: :post, class: 'btn has-tooltip', title: @issue.to_branch_name, disabled: 'disabled' do
         .checking
-          %i.fa.fa-spinner.fa-spin
+          = icon('spinner spin')
           Checking branches
-        .available(style="display: none")
-          %i.fa.fa-code-fork
+        .available.hide
           New branch
-        .unavailable(style="display: none")
-          %i.fa.fa-exclamation-triangle
+        .unavailable.hide
+          = icon('exclamation-triangle')
           New branch unavailable
diff --git a/app/views/projects/issues/_related_branches.html.haml b/app/views/projects/issues/_related_branches.html.haml
index 5f9d2919982e9a1a55420485be14ab2dfda531f7..c6fc499a7b8b348378bd4f8c1da9473ae12fd472 100644
--- a/app/views/projects/issues/_related_branches.html.haml
+++ b/app/views/projects/issues/_related_branches.html.haml
@@ -5,10 +5,10 @@
     - @related_branches.each do |branch|
       %li
         - sha = @project.repository.find_branch(branch).target
-        - ci_commit = @project.ci_commit(sha, branch) if sha
-        - if ci_commit
+        - pipeline = @project.pipeline(sha, branch) if sha
+        - if pipeline
           %span.related-branch-ci-status
-            = render_pipeline_status(ci_commit)
+            = render_pipeline_status(pipeline)
         %span.related-branch-info
           %strong
             = link_to namespace_project_compare_path(@project.namespace, @project, from: @project.default_branch, to: branch), class: "label-branch" do
diff --git a/app/views/projects/issues/edit.html.haml b/app/views/projects/issues/edit.html.haml
index 20216297d2558fd57933a9aefd006e68e621a3e2..7cf1923456efe94da56446267b6a82a2e8e265fc 100644
--- a/app/views/projects/issues/edit.html.haml
+++ b/app/views/projects/issues/edit.html.haml
@@ -1,5 +1,4 @@
 - page_title "Edit", "#{@issue.title} (##{@issue.iid})", "Issues"
-= render "header_title"
 
 %h3.page-title
   Edit Issue ##{@issue.iid}
diff --git a/app/views/projects/issues/index.atom.builder b/app/views/projects/issues/index.atom.builder
index ee8a94146572c72f9d37111518ea1772cefb2204..36957560de0cf6466c0aea564ee3050d417d8e4d 100644
--- a/app/views/projects/issues/index.atom.builder
+++ b/app/views/projects/issues/index.atom.builder
@@ -4,9 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
   xml.link    href: namespace_project_issues_url(@project.namespace, @project, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
   xml.link    href: namespace_project_issues_url(@project.namespace, @project), rel: "alternate", type: "text/html"
   xml.id      namespace_project_issues_url(@project.namespace, @project)
-  xml.updated @issues.first.created_at.xmlschema if @issues.any?
+  xml.updated @issues.first.created_at.xmlschema if @issues.reorder(nil).any?
 
-  @issues.each do |issue|
-    issue_to_atom(xml, issue)
-  end
+  xml << render(partial: 'issues/issue', collection: @issues) if @issues.reorder(nil).any?
 end
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index efa7642b2dc5b9056db5680b8ac347535e262fce..cd876b5ea62d5aa58f65fc54278c44dd90108dcc 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -1,25 +1,26 @@
+- @no_container = true
 - page_title "Issues"
-= render "header_title"
+= render "projects/issues/head"
 
 = content_for :meta_tags do
   - if current_user
     = auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
 
-.top-area
-  = render 'shared/issuable/nav', type: :issues
-  .nav-controls
-    - if current_user
-      = link_to namespace_project_issues_path(@project.namespace, @project, :atom, { private_token: current_user.private_token }), class: 'btn append-right-10' do
-        = icon('rss')
-        %span.icon-label
-          Subscribe
+%div{ class: (container_class) }
+  .top-area
+    = render 'shared/issuable/nav', type: :issues
+    .nav-controls
+      - if current_user
+        = link_to namespace_project_issues_path(@project.namespace, @project, :atom, { private_token: current_user.private_token }), class: 'btn append-right-10' do
+          = icon('rss')
+          %span.icon-label
+            Subscribe
       = render 'shared/issuable/search_form', path: namespace_project_issues_path(@project.namespace, @project)
-    - if can? current_user, :create_issue, @project
-      = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { assignee_id: @issuable_finder.assignee.try(:id), milestone_id: @issuable_finder.milestones.try(:first).try(:id) }), class: "btn btn-new", title: "New Issue", id: "new_issue_link" do
-        = icon('plus')
-        New Issue
+      - if can? current_user, :create_issue, @project
+        = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { assignee_id: @issuable_finder.assignee.try(:id), milestone_id: @issuable_finder.milestones.try(:first).try(:id) }), class: "btn btn-new", title: "New Issue", id: "new_issue_link" do
+          New Issue
 
-= render 'shared/issuable/filter', type: :issues
+  = render 'shared/issuable/filter', type: :issues
 
-.issues-holder
-  = render "issues"
+  .issues-holder
+    = render "issues"
diff --git a/app/views/projects/issues/new.html.haml b/app/views/projects/issues/new.html.haml
index b317a0c1cf4429721905ab89b4934fcdc0c8cb22..e8aae0f47e2652ebf3c1d818c98372d69a4a0149 100644
--- a/app/views/projects/issues/new.html.haml
+++ b/app/views/projects/issues/new.html.haml
@@ -1,5 +1,4 @@
 - page_title "New Issue"
-= render "header_title"
 
 %h3.page-title
   New Issue
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index bde80bbb54b228667f89a3ce599c20caed293d58..9b6a97c0959f83c5de36c29cd089b5ada45a0b2f 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -1,7 +1,6 @@
 - page_title           "#{@issue.title} (##{@issue.iid})", "Issues"
 - page_description     @issue.description
 - page_card_attributes @issue.card_attributes
-- header_title         project_title(@project, "Issues", namespace_project_issues_path(@project.namespace, @project))
 
 .clearfix.detail-page-header
   .issuable-header
@@ -39,26 +38,24 @@
               %li
                 = link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue)
         - if can?(current_user, :create_issue, @project)
-          = link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'hidden-xs hidden-sm btn btn-nr btn-grouped new-issue-link btn-success', title: 'New issue', id: 'new_issue_link' do
-            = icon('plus')
+          = link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'hidden-xs hidden-sm btn btn-grouped new-issue-link btn-success', title: 'New issue', id: 'new_issue_link' do
             New issue
         - if can?(current_user, :update_issue, @issue)
-          = link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-nr btn-grouped btn-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: "hidden-xs hidden-sm btn btn-nr btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
-          = link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-nr btn-grouped issuable-edit' do
-            = icon('pencil-square-o')
+          = link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-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: "hidden-xs hidden-sm btn btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
+          = link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit' do
             Edit
 
 
 .issue-details.issuable-details
   .detail-page-description.content-block
     %h2.title
-      = markdown escape_once(@issue.title), pipeline: :single_line
+      = markdown escape_once(@issue.title), pipeline: :single_line, author: @issue.author
     - if @issue.description.present?
       .description{ class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : '' }
         .wiki
           = preserve do
-            = markdown(@issue.description, cache_key: [@issue, "description"])
+            = markdown(@issue.description, cache_key: [@issue, "description"], author: @issue.author)
         %textarea.hidden.js-task-list-field
           = @issue.description
     = edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue_edited_ago')
@@ -71,7 +68,7 @@
 
   .content-block.content-block-small
     = render 'new_branch'
-    = render 'votes/votes_block', votable: @issue
+    = render 'award_emoji/awards_block', awardable: @issue, inline: true
 
   %section.issuable-discussion
     = render 'projects/issues/discussion'
diff --git a/app/views/projects/labels/_header_title.html.haml b/app/views/projects/labels/_header_title.html.haml
deleted file mode 100644
index abe28da483b7a9a4134b79c11b717a4bb82b07e7..0000000000000000000000000000000000000000
--- a/app/views/projects/labels/_header_title.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-- header_title project_title(@project, "Labels", namespace_project_labels_path(@project.namespace, @project))
diff --git a/app/views/projects/labels/_label.html.haml b/app/views/projects/labels/_label.html.haml
index 8bf544b83710b804438ddad6931be20387313b31..73c6f2a046c5f18115c0527a6686b4c3a830018b 100644
--- a/app/views/projects/labels/_label.html.haml
+++ b/app/views/projects/labels/_label.html.haml
@@ -1,28 +1,50 @@
-%li{id: dom_id(label)}
+- label_css_id = dom_id(label)
+%li{id: label_css_id, data: { id: label.id } }
   = render "shared/label_row", label: label
 
-  .pull-info-right
-    %span.append-right-20
-      = link_to_label(label, type: :merge_request) do
-        = pluralize label.open_merge_requests_count, 'merge request'
+  .visible-xs.visible-sm-inline-block.visible-md-inline-block.dropdown
+    %button.btn.btn-default.label-options-toggle{ data: { toggle: "dropdown" } }
+      Options
+      %span.caret
+    .dropdown-menu.dropdown-menu-align-right
+      %ul
+        %li
+          = link_to_label(label, type: :merge_request) do
+            = pluralize label.open_merge_requests_count, 'merge request'
+        %li
+          = link_to_label(label) do
+            = pluralize label.open_issues_count(current_user), 'open issue'
+        - if current_user
+          %li.label-subscription{ data: { url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) } }
+            %a.js-subscribe-button.label-subscribe-button.subscription-status{ role: "button", href: "#", data: { toggle: "tooltip", status: label_subscription_status(label) } }
+              %span= label_subscription_toggle_button_text(label)
+        - if can? current_user, :admin_label, @project
+          %li
+            = link_to "Edit", edit_namespace_project_label_path(@project.namespace, @project, label)
+          %li
+            = link_to "Delete", namespace_project_label_path(@project.namespace, @project, label), title: "Delete", method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"}
 
-    %span.append-right-20
-      = link_to_label(label) do
-        = pluralize label.open_issues_count(current_user), 'open issue'
+  .pull-right.hidden-xs.hidden-sm.hidden-md
+    = link_to_label(label, type: :merge_request, css_class: 'btn btn-transparent btn-action') do
+      = pluralize label.open_merge_requests_count, 'merge request'
+    = link_to_label(label, css_class: 'btn btn-transparent btn-action') do
+      = pluralize label.open_issues_count(current_user), 'open issue'
 
     - if current_user
-      .label-subscription{data: {url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label)}}
-        .subscription-status{data: {status: label_subscription_status(label)}}
-
-        %button.js-subscribe-button.label-subscribe-button.btn.action-buttons{ type: "button", data: { toggle: "tooltip" } }
-          %span= label_subscription_toggle_button_text(label)
+      .label-subscription.inline{ data: { url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) } }
+        %button.js-subscribe-button.label-subscribe-button.btn.btn-transparent.btn-action.subscription-status{ type: "button", title: label_subscription_toggle_button_text(label), data: { toggle: "tooltip", status: label_subscription_status(label) } }
+          %span.sr-only= label_subscription_toggle_button_text(label)
+          = icon('eye', class: 'label-subscribe-button-icon')
+          = icon('spinner spin', class: 'label-subscribe-button-loading')
 
     - if can? current_user, :admin_label, @project
-      = link_to edit_namespace_project_label_path(@project.namespace, @project, label), title: "Edit", class: 'btn action-buttons', data: {toggle: "tooltip"} do
-        %i.fa.fa-pencil-square-o
-      = link_to namespace_project_label_path(@project.namespace, @project, label), title: "Delete", class: 'btn action-buttons remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?", toggle: "tooltip"} do
-        %i.fa.fa-trash-o
+      = link_to edit_namespace_project_label_path(@project.namespace, @project, label), title: "Edit", class: 'btn btn-transparent btn-action', data: {toggle: "tooltip"} do
+        %span.sr-only Edit
+        = icon('pencil-square-o')
+      = link_to namespace_project_label_path(@project.namespace, @project, label), title: "Delete", class: 'btn btn-transparent btn-action remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?", toggle: "tooltip"} do
+        %span.sr-only Delete
+        = icon('trash-o')
 
-- if current_user
-  :javascript
-    new Subscription('##{dom_id(label)} .label-subscription');
+  - if current_user
+    :javascript
+      new Subscription('##{dom_id(label)} .label-subscription');
diff --git a/app/views/projects/labels/edit.html.haml b/app/views/projects/labels/edit.html.haml
index 675a805e12fe1d4caec6a35d80917716cc876597..6901ba13ab70e745531cb37c424a713769ef6921 100644
--- a/app/views/projects/labels/edit.html.haml
+++ b/app/views/projects/labels/edit.html.haml
@@ -1,5 +1,4 @@
 - page_title "Edit", @label.name, "Labels"
-= render "header_title"
 
 %h3.page-title
   Edit Label
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index cc41130a9dca38edb907f6b901052dd79e66a72c..aa4d69550ec6b6cfc728e03970ffbc5b7b441741 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -1,23 +1,38 @@
+- @no_container = true
 - page_title "Labels"
-= render "header_title"
+- hide_class = ''
+= render "projects/issues/head"
 
-.top-area
-  .nav-text
-    Labels can be applied to issues and merge requests.
-  .nav-controls
-    - if can? current_user, :admin_label, @project
-      = link_to new_namespace_project_label_path(@project.namespace, @project), class: "btn btn-new" do
-        = icon('plus')
-        New label
+%div{ class: (container_class) }
+  .top-area.adjust
+    .nav-text
+      Labels can be applied to issues and merge requests. Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging.
 
-.labels
-  - if @labels.present?
-    %ul.content-list.manage-labels-list
-      = render @labels
-    = paginate @labels, theme: 'gitlab'
-  - else
-    .nothing-here-block
-      - if can? current_user, :admin_label, @project
-        Create first label or #{link_to 'generate', generate_namespace_project_labels_path(@project.namespace, @project), method: :post} default set of labels
+    .nav-controls
+      - if can?(current_user, :admin_label, @project)
+        = link_to new_namespace_project_label_path(@project.namespace, @project), class: "btn btn-new" do
+          New label
+
+  .labels
+    - if can?(current_user, :admin_label, @project)
+      -# Only show it in the first page
+      - hide = @project.labels.empty? || (params[:page].present? && params[:page] != '1')
+      .prioritized-labels{ class: ('hide' if hide) }
+        %h5 Prioritized Labels
+        %ul.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_namespace_project_labels_path(@project.namespace, @project) }
+          %p.empty-message{ class: ('hidden' unless @prioritized_labels.empty?) } No prioritized labels yet
+          - if @prioritized_labels.present?
+            = render @prioritized_labels
+    .other-labels
+      - if can?(current_user, :admin_label, @project)
+        %h5{ class: ('hide' if hide) } Other Labels
+      - if @labels.present?
+        %ul.content-list.manage-labels-list.js-other-labels
+          = render @labels
+        = paginate @labels, theme: 'gitlab'
       - else
-        No labels created
+        .nothing-here-block
+          - if can?(current_user, :admin_label, @project)
+            Create a label or #{link_to 'generate a default set of labels', generate_namespace_project_labels_path(@project.namespace, @project), method: :post}.
+          - else
+            No labels created
diff --git a/app/views/projects/labels/new.html.haml b/app/views/projects/labels/new.html.haml
index e20fd7d6891fc40d492bbff755b8d03d6ea51af8..49ddf9016192d95aa7d10e45090c89b2a41eb511 100644
--- a/app/views/projects/labels/new.html.haml
+++ b/app/views/projects/labels/new.html.haml
@@ -1,5 +1,4 @@
 - page_title "New Label"
-= render "header_title"
 
 %h3.page-title
   New Label
diff --git a/app/views/projects/merge_requests/_head.html.haml b/app/views/projects/merge_requests/_head.html.haml
deleted file mode 100644
index 19e4dab874bcd9e805232b5abf8bd6aa1c2f2ab7..0000000000000000000000000000000000000000
--- a/app/views/projects/merge_requests/_head.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-.top-tabs
-  = link_to namespace_project_merge_requests_path(@project.namespace, @project), class: "tab #{'active' if current_page?(namespace_project_merge_requests_path(@project.namespace, @project)) }" do
-    %span
-    Merge Requests
-
diff --git a/app/views/projects/merge_requests/_header_title.html.haml b/app/views/projects/merge_requests/_header_title.html.haml
deleted file mode 100644
index 669a9b06bdf5a15af143a77fac7e88fedb93b6bb..0000000000000000000000000000000000000000
--- a/app/views/projects/merge_requests/_header_title.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-- header_title project_title(@project, "Merge Requests", namespace_project_merge_requests_path(@project.namespace, @project))
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 2c54171c6bd53c4184faf0f9d7ee1c8e91590c5a..5029b365f934e193de54d968f3f1046548d29cc9 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -1,7 +1,7 @@
 %li{ class: mr_css_classes(merge_request) }
   .merge-request-title.title
     %span.merge-request-title-text
-      = link_to_gfm merge_request.title, merge_request_path(merge_request)
+      = link_to merge_request.title, merge_request_path(merge_request)
     %ul.controls
       - if merge_request.merged?
         %li
@@ -11,9 +11,9 @@
           = icon('ban')
           CLOSED
 
-      - if merge_request.ci_commit
+      - if merge_request.pipeline
         %li
-          = render_pipeline_status(merge_request.ci_commit)
+          = render_pipeline_status(merge_request.pipeline)
 
       - if merge_request.open? && merge_request.broken?
         %li
@@ -35,7 +35,7 @@
           = icon('thumbs-down')
           = downvotes
 
-      - note_count = merge_request.mr_and_commit_notes.user.nonawards.count
+      - note_count = merge_request.mr_and_commit_notes.user.count
       %li
         = link_to merge_request_path(merge_request, anchor: 'notes'), class: ('merge-request-no-comments' if note_count.zero?) do
           = icon('comments')
diff --git a/app/views/projects/merge_requests/_merge_requests.html.haml b/app/views/projects/merge_requests/_merge_requests.html.haml
index 5473fa191662af30e0ecc02be3652e041e453e4f..446887774a4e343f223bfc7533de3bde36fb5dc1 100644
--- a/app/views/projects/merge_requests/_merge_requests.html.haml
+++ b/app/views/projects/merge_requests/_merge_requests.html.haml
@@ -6,4 +6,3 @@
 
 - if @merge_requests.present?
   = 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 18b3f9e1549a0218e15e4630d93fbe84dd050f14..a5e67b95727f3f9714b39b09fcb8bb044ed6101b 100644
--- a/app/views/projects/merge_requests/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/_new_submit.html.haml
@@ -23,7 +23,7 @@
       = link_to url_for(params), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do
         Commits
         %span.badge= @commits.size
-    - if @ci_commit
+    - if @pipeline
       %li.builds-tab.active
         = link_to url_for(params), data: {target: 'div#builds', action: 'builds', toggle: 'tab'} do
           Builds
@@ -43,7 +43,7 @@
           %p To preserve performance the line changes are not shown.
       - else
         = render "projects/diffs/diffs", diffs: @diffs, project: @project, diff_refs: @merge_request.diff_refs, show_whitespace_toggle: false
-    - if @ci_commit
+    - if @pipeline
       #builds.builds.tab-pane
         = render "projects/merge_requests/show/builds"
 
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index 290753d57c6edacdbaed8f5d5f8e0112649e3f41..c4df8bd504f31fe92cebe97a40ec01c84495bf90 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -1,7 +1,6 @@
 - page_title           "#{@merge_request.title} (#{@merge_request.to_reference})", "Merge Requests"
 - page_description     @merge_request.description
 - page_card_attributes @merge_request.card_attributes
-- header_title project_title(@project, "Merge Requests", namespace_project_merge_requests_path(@project.namespace, @project))
 
 - if diff_view == 'parallel'
   - fluid_layout true
@@ -15,13 +14,11 @@
       - if @merge_request.open?
         .pull-right
           - if @merge_request.source_branch_exists?
-            = link_to "#modal_merge_info", class: "btn btn-sm", "data-toggle" => "modal" do
-              = icon('cloud-download fw')
+            = link_to "#modal_merge_info", class: "btn inline btn-grouped btn-sm", "data-toggle" => "modal" do
               Check out branch
 
           %span.dropdown
             %a.btn.btn-sm.dropdown-toggle{ data: {toggle: :dropdown} }
-              = icon('download')
               Download as
               %span.caret
             %ul.dropdown-menu
@@ -50,12 +47,12 @@
         %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
-            %span.badge= @merge_request.mr_and_commit_notes.user.nonawards.count
+            %span.badge= @merge_request.mr_and_commit_notes.user.count
         %li.commits-tab
           = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do
             Commits
             %span.badge= @commits.size
-        - if @ci_commit
+        - if @pipeline
           %li.builds-tab
             = link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#builds', action: 'builds', toggle: 'tab'} do
               Builds
@@ -68,7 +65,7 @@
       .tab-content
         #notes.notes.tab-pane.voting_notes
           .content-block.content-block-small.oneline-block
-            = render 'votes/votes_block', votable: @merge_request
+            = render 'award_emoji/awards_block', awardable: @merge_request, inline: true
 
           .row
             %section.col-md-12
diff --git a/app/views/projects/merge_requests/dropdowns/_branch.html.haml b/app/views/projects/merge_requests/dropdowns/_branch.html.haml
index ba8d9a5835c010c18c96dbcb3fcf551e1caf1be0..a60c445aa518a280beb9645dad0a2df586c79ea4 100644
--- a/app/views/projects/merge_requests/dropdowns/_branch.html.haml
+++ b/app/views/projects/merge_requests/dropdowns/_branch.html.haml
@@ -1,5 +1,5 @@
 %ul
   - branches.each do |branch|
     %li
-      %a{ href: '#', class: "#{('is-active' if selected == branch)}", data: { id: branch } }
+      %a{ href: '#', class: "#{('is-active' if selected == branch)}", title: branch, data: { id: branch } }
         = branch
diff --git a/app/views/projects/merge_requests/edit.html.haml b/app/views/projects/merge_requests/edit.html.haml
index b31ea5e532157afd0421d130ffced1ab4f7aaed7..03159f123f3764b5413392e3442d012e70952844 100644
--- a/app/views/projects/merge_requests/edit.html.haml
+++ b/app/views/projects/merge_requests/edit.html.haml
@@ -1,5 +1,4 @@
 - page_title "Edit", "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests"
-= render "header_title"
 
 %h3.page-title
   Edit Merge Request #{@merge_request.to_reference}
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index e56a44e0a79dbe108215599b8a336262b8c68b14..9f948d41ddaf8a963d3b0388d60d9da9d4bf7a0a 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -1,20 +1,20 @@
+- @no_container = true
 - page_title "Merge Requests"
-= render "header_title"
-
+= render "projects/issues/head"
 = render 'projects/last_push'
 
-.top-area
-  = render 'shared/issuable/nav', type: :merge_requests
-  .nav-controls
-    = render 'shared/issuable/search_form', path: namespace_project_merge_requests_path(@project.namespace, @project)
+%div{ class: (container_class) }
+  .top-area
+    = render 'shared/issuable/nav', type: :merge_requests
+    .nav-controls
+      = render 'shared/issuable/search_form', path: namespace_project_merge_requests_path(@project.namespace, @project)
 
-    - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
-    - if merge_project
-      = link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project), class: "btn btn-new", title: "New Merge Request" do
-        = icon('plus')
-        New Merge Request
+      - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
+      - if merge_project
+        = link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project), class: "btn btn-new", title: "New Merge Request" do
+          New Merge Request
 
-= render 'shared/issuable/filter', type: :merge_requests
+  = render 'shared/issuable/filter', type: :merge_requests
 
-.merge-requests-holder
-  = render 'merge_requests'
+  .merge-requests-holder
+    = render 'merge_requests'
diff --git a/app/views/projects/merge_requests/invalid.html.haml b/app/views/projects/merge_requests/invalid.html.haml
index f5bf16ef3adb1efce0b655b0f0a8391d58001782..a00d3128ffef70c8d74e3f77f523e2ac5b81b303 100644
--- a/app/views/projects/merge_requests/invalid.html.haml
+++ b/app/views/projects/merge_requests/invalid.html.haml
@@ -1,5 +1,4 @@
 - page_title "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests"
-= render "header_title"
 
 .merge-request
   = render "projects/merge_requests/show/mr_title"
diff --git a/app/views/projects/merge_requests/merge.js.haml b/app/views/projects/merge_requests/merge.js.haml
index 3085009f37e43e5e366bce0211b0f7e32237bac3..999a954450ca0e43d9c2f8380c6f100d009dc6fb 100644
--- a/app/views/projects/merge_requests/merge.js.haml
+++ b/app/views/projects/merge_requests/merge.js.haml
@@ -8,6 +8,9 @@
 - when :hook_validation_error
   :plain
     $('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/error'))}");
+- when :sha_mismatch
+  :plain
+    $('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/sha_mismatch'))}");
 - else
   :plain
     $('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/reload'))}");
diff --git a/app/views/projects/merge_requests/new.html.haml b/app/views/projects/merge_requests/new.html.haml
index d259968030e92b0d7e02c0ef293d4cfca70f0f44..2e798ce780a7102593760030e9cc4e93a98e74da 100644
--- a/app/views/projects/merge_requests/new.html.haml
+++ b/app/views/projects/merge_requests/new.html.haml
@@ -1,5 +1,4 @@
 - page_title "New Merge Request"
-= render "header_title"
 
 - if @merge_request.can_be_created && !params[:change_branches]
   = render 'new_submit'
diff --git a/app/views/projects/merge_requests/show/_builds.html.haml b/app/views/projects/merge_requests/show/_builds.html.haml
index a116ffe2e151ed27664ff47d3d40d784e85cc1e9..81de60f116c2cb7a3b641d00432d547a17adf104 100644
--- a/app/views/projects/merge_requests/show/_builds.html.haml
+++ b/app/views/projects/merge_requests/show/_builds.html.haml
@@ -1,2 +1,2 @@
-= render "projects/commit/ci_commit", ci_commit: @ci_commit, link_to_commit: true
+= render "projects/commit/pipeline", pipeline: @pipeline, link_to_commit: true
 
diff --git a/app/views/projects/merge_requests/show/_commits.html.haml b/app/views/projects/merge_requests/show/_commits.html.haml
index a8f09f855d48d41c57c67ca639ea87432ad21d6c..0b05785430b10f0d34593668c96679686b130f92 100644
--- a/app/views/projects/merge_requests/show/_commits.html.haml
+++ b/app/views/projects/merge_requests/show/_commits.html.haml
@@ -2,4 +2,5 @@
   = icon("sort-amount-desc")
   Most recent commits displayed first
 
-= render "projects/commits/commits", project: @merge_request.project
+%ol#commits-list.list-unstyled
+  = render "projects/commits/commits", project: @merge_request.project
diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml
index a23bd8d18d0512f8969d40d6b822c02d52d2a1f9..ebf18f6ac8598be8bdcedec09798ee84c6b9e06a 100644
--- a/app/views/projects/merge_requests/show/_mr_box.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_box.html.haml
@@ -1,13 +1,13 @@
 .detail-page-description.content-block
   %h2.title
-    = markdown escape_once(@merge_request.title), pipeline: :single_line
+    = markdown escape_once(@merge_request.title), pipeline: :single_line, author: @merge_request.author
 
   %div
     - if @merge_request.description.present?
       .description{class: can?(current_user, :update_merge_request, @merge_request) ? 'js-task-list-container' : ''}
         .wiki
           = preserve do
-            = markdown(@merge_request.description, cache_key: [@merge_request, "description"])
+            = markdown(@merge_request.description, cache_key: [@merge_request, "description"], author: @merge_request.author)
         %textarea.hidden.js-task-list-field
           = @merge_request.description
 
diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml
index 36c275e8be11efdaa2adef6220abf1148ea2db05..5bf5210aeab390a644157536af4a538b59dd0e50 100644
--- a/app/views/projects/merge_requests/show/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_title.html.haml
@@ -25,8 +25,7 @@
               = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: 'reopen-mr-link', title: 'Reopen merge request'
             %li
               = link_to 'Edit', edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'issuable-edit'
-        = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: "hidden-xs hidden-sm btn btn-nr btn-grouped btn-close #{issue_button_visibility(@merge_request, true)}", title: 'Close merge request'
-        = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "hidden-xs hidden-sm btn btn-nr btn-grouped btn-reopen reopen-mr-link #{issue_button_visibility(@merge_request, false)}", title: 'Reopen merge request'
-        = link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "hidden-xs hidden-sm btn btn-nr btn-grouped issuable-edit" do
-          = icon('pencil-square-o')
+        = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: "hidden-xs hidden-sm btn btn-grouped btn-close #{issue_button_visibility(@merge_request, true)}", title: 'Close merge request'
+        = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "hidden-xs hidden-sm btn btn-grouped btn-reopen reopen-mr-link #{issue_button_visibility(@merge_request, false)}", title: 'Reopen merge request'
+        = link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "hidden-xs hidden-sm btn btn-grouped issuable-edit" do
           Edit
diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml
index 4d3817546103f1dee29139894b16fd184f5e439d..08a38d283d23889e3096a2d20026aa0d50fc1be9 100644
--- a/app/views/projects/merge_requests/widget/_heading.html.haml
+++ b/app/views/projects/merge_requests/widget/_heading.html.haml
@@ -1,7 +1,7 @@
-- if @ci_commit
+- if @pipeline
   .mr-widget-heading
     - %w[success skipped canceled failed running pending].each do |status|
-      .ci_widget{ class: "ci-#{status}", style: ("display:none" unless @ci_commit.status == status) }
+      .ci_widget{ class: "ci-#{status}", style: ("display:none" unless @pipeline.status == status) }
         = ci_icon_for_status(status)
         %span
           CI build
@@ -9,7 +9,7 @@
         for
         - commit = @merge_request.last_commit
         = succeed "." do
-          = link_to @ci_commit.short_sha, namespace_project_commit_path(@merge_request.source_project.namespace, @merge_request.source_project, @ci_commit.sha), class: "monospace"
+          = link_to @pipeline.short_sha, namespace_project_commit_path(@merge_request.source_project.namespace, @merge_request.source_project, @pipeline.sha), class: "monospace"
         %span.ci-coverage
         = link_to "View details", builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "js-show-tab", data: {action: 'builds'}
 
diff --git a/app/views/projects/merge_requests/widget/_merged.html.haml b/app/views/projects/merge_requests/widget/_merged.html.haml
index ec4beae9727b3ec07e64d584ae92e11662843b36..19b5d0ff0664f5b64a8acb04e372fe0ecf725e4b 100644
--- a/app/views/projects/merge_requests/widget/_merged.html.haml
+++ b/app/views/projects/merge_requests/widget/_merged.html.haml
@@ -6,46 +6,29 @@
       - if @merge_request.merge_event
         by #{link_to_member(@project, @merge_request.merge_event.author, avatar: true)}
         #{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
-    %div
-      - if !@merge_request.source_branch_exists? || (params[:delete_source] == 'true')
+    - if !@merge_request.source_branch_exists? || (params[:delete_source] == 'true')
+      %p
+        The changes were merged into
+        #{link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch"}.
+        The source branch has been removed.
+      = render 'projects/merge_requests/widget/merged_buttons'
+    - elsif @merge_request.can_remove_source_branch?(current_user)
+      .remove_source_branch_widget
         %p
           The changes were merged into
           #{link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch"}.
-          The source branch has been removed.
-        = render 'projects/merge_requests/widget/merged_buttons'
-      - elsif @merge_request.can_remove_source_branch?(current_user)
-        .remove_source_branch_widget
-          %p
-            The changes were merged into
-            #{link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch"}.
-            You can remove the source branch now.
-          = render 'projects/merge_requests/widget/merged_buttons', source_branch_exists: true
-        .remove_source_branch_widget.failed.hide
-          %p
-            Failed to remove source branch '#{@merge_request.source_branch}'.
-
-        .remove_source_branch_in_progress.hide
-          %p
-            = icon('spinner spin')
-            Removing source branch '#{@merge_request.source_branch}'. Please wait, this page will be automatically reloaded.
-
-        :javascript
-          $('.remove_source_branch').on('click', function() {
-            $('.remove_source_branch_widget').hide();
-            $('.remove_source_branch_in_progress').show();
-          });
-
-          $(".remove_source_branch").on("ajax:success", function (e, data, status, xhr) {
-            location.reload();
-          });
+          You can remove the source branch now.
+        = render 'projects/merge_requests/widget/merged_buttons', source_branch_exists: true
+      .remove_source_branch_widget.failed.hide
+        %p
+          Failed to remove source branch '#{@merge_request.source_branch}'.
 
-          $(".remove_source_branch").on("ajax:error", function (e, data, status, xhr) {
-            $('.remove_source_branch_widget').hide();
-            $('.remove_source_branch_in_progress').hide();
-            $('.remove_source_branch_widget.failed').show();
-          });
-      - else
+      .remove_source_branch_in_progress.hide
         %p
-          The changes were merged into
-          #{link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch"}.
-        = render 'projects/merge_requests/widget/merged_buttons'
+          = icon('spinner spin')
+          Removing source branch '#{@merge_request.source_branch}'. Please wait, this page will be automatically reloaded.
+    - else
+      %p
+        The changes were merged into
+        #{link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch"}.
+      = render 'projects/merge_requests/widget/merged_buttons'
diff --git a/app/views/projects/merge_requests/widget/_merged_buttons.haml b/app/views/projects/merge_requests/widget/_merged_buttons.haml
index 56167509af97e75ad6bda88ef27b8a3f52f48c6d..d836a2535070f86db76500611509b50de4126492 100644
--- a/app/views/projects/merge_requests/widget/_merged_buttons.haml
+++ b/app/views/projects/merge_requests/widget/_merged_buttons.haml
@@ -3,9 +3,9 @@
 - mr_can_be_cherry_picked = @merge_request.can_be_cherry_picked?
 
 - if can_remove_source_branch || mr_can_be_reverted || mr_can_be_cherry_picked
-  .btn-group
+  .clearfix.merged-buttons
     - if can_remove_source_branch
-      = link_to namespace_project_branch_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request.source_branch), remote: true, method: :delete, class: "btn btn-default btn-grouped btn-sm remove_source_branch" do
+      = link_to namespace_project_branch_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request.source_branch), remote: true, method: :delete, class: "btn btn-default btn-sm remove_source_branch" do
         = icon('trash-o')
         Remove Source Branch
     - if mr_can_be_reverted
diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml
index 943d2f89ed4951158249e6349dfa08a4ae373d77..c407c8b2e8146e836b8400da015d77593fb65d6c 100644
--- a/app/views/projects/merge_requests/widget/_open.html.haml
+++ b/app/views/projects/merge_requests/widget/_open.html.haml
@@ -21,6 +21,8 @@
       = render 'projects/merge_requests/widget/open/merge_when_build_succeeds'
     - elsif !@merge_request.can_be_merged_by?(current_user)
       = render 'projects/merge_requests/widget/open/not_allowed'
+    - elsif !@merge_request.mergeable_ci_state? && @pipeline && @pipeline.failed?
+      = render 'projects/merge_requests/widget/open/build_failed'
     - elsif @merge_request.must_be_rebased?
       = render 'projects/merge_requests/widget/open/rebase'
     - else
@@ -32,7 +34,7 @@
         %i.fa.fa-check
         Accepting this merge request will close #{"issue".pluralize(@closes_issues.size)}
         = succeed '.' do
-          != markdown issues_sentence(@closes_issues), pipeline: :gfm
+          != markdown issues_sentence(@closes_issues), pipeline: :gfm, author: @merge_request.author
 
   - if @merge_request.approvals.any?
     .mr-widget-footer.approved-by-users
diff --git a/app/views/projects/merge_requests/widget/_show.html.haml b/app/views/projects/merge_requests/widget/_show.html.haml
index 3c68d61c4b59151f4061e2dbe2cb81d7124522de..d9efe81701f222fb81d25b86c972414b420a5198 100644
--- a/app/views/projects/merge_requests/widget/_show.html.haml
+++ b/app/views/projects/merge_requests/widget/_show.html.haml
@@ -13,7 +13,7 @@
     check_enable: #{@merge_request.unchecked? ? "true" : "false"},
     ci_status_url: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
     gitlab_icon: "#{asset_path 'gitlab_logo.png'}",
-    ci_status: "",
+    ci_status: "#{@merge_request.pipeline ? @merge_request.pipeline.status : ''}",
     ci_message: {
       normal: "Build {{status}} for \"{{title}}\"",
       preparing: "{{status}} build for \"{{title}}\""
@@ -26,4 +26,10 @@
     builds_path: "#{builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}"
   };
 
+  if (typeof merge_request_widget !== 'undefined') {
+    clearInterval(merge_request_widget.fetchBuildStatusInterval);
+    merge_request_widget.cancelPolling();
+    merge_request_widget.clearEventListeners();
+  }
+
   merge_request_widget = new MergeRequestWidget(opts);
diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml
index 87374432ab56f4ed8f63c2e685a663d98881eaff..8977bfee5f75fed6e368c74f9a8ca0e8b590183b 100644
--- a/app/views/projects/merge_requests/widget/open/_accept.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml
@@ -1,27 +1,29 @@
-- status_class = @ci_commit ? " ci-#{@ci_commit.status}" : nil
+- status_class = @pipeline ? " ci-#{@pipeline.status}" : nil
 
 = form_for [:merge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-quick-submit js-requires-input' } do |f|
   = hidden_field_tag :authenticity_token, form_authenticity_token
+  = hidden_field_tag :sha, @merge_request.source_sha
   .accept-merge-holder.clearfix.js-toggle-container
     .clearfix
       .accept-action
-        - if @ci_commit && @ci_commit.active?
+        - if @pipeline && @pipeline.active?
           %span.btn-group
             = button_tag class: "btn btn-create js-merge-button merge_when_build_succeeds" do
               Merge When Build Succeeds
-            = button_tag class: "btn btn-success dropdown-toggle", 'data-toggle' => 'dropdown' do
-              %span.caret
-              %span.sr-only
-                Select Merge Moment
-            %ul.js-merge-dropdown.dropdown-menu.dropdown-menu-right{ role: 'menu' }
-              %li
-                = link_to "#", class: "merge_when_build_succeeds" do
-                  = icon('check fw')
-                  Merge When Build Succeeds
-              %li
-                = link_to "#", class: "accept_merge_request" do
-                  = icon('warning fw')
-                  Merge Immediately
+            - unless @project.only_allow_merge_if_build_succeeds?
+              = button_tag class: "btn btn-success dropdown-toggle", 'data-toggle' => 'dropdown' do
+                %span.caret
+                %span.sr-only
+                  Select Merge Moment
+              %ul.js-merge-dropdown.dropdown-menu.dropdown-menu-right{ role: 'menu' }
+                %li
+                  = link_to "#", class: "merge_when_build_succeeds" do
+                    = icon('check fw')
+                    Merge When Build Succeeds
+                %li
+                  = link_to "#", class: "accept_merge_request" do
+                    = icon('warning fw')
+                    Merge Immediately
         - else
           = f.button class: "btn btn-create btn-grouped js-merge-button accept_merge_request #{status_class}" do
             Accept Merge Request
diff --git a/app/views/projects/merge_requests/widget/open/_build_failed.html.haml b/app/views/projects/merge_requests/widget/open/_build_failed.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..14f51af536080a74885a1dcb2dc39de640fb99e2
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/open/_build_failed.html.haml
@@ -0,0 +1,6 @@
+%h4
+  = icon('exclamation-triangle')
+  The build for this merge request failed
+
+%p
+  Please retry the build or push a new commit to fix the failure.
diff --git a/app/views/projects/merge_requests/widget/open/_conflicts.html.haml b/app/views/projects/merge_requests/widget/open/_conflicts.html.haml
index 6d0d6854b4532a93be198dd5a53140c142305bc1..06ab0a3fa0020f20ab8e7fbd796ae8e12f9d6ffc 100644
--- a/app/views/projects/merge_requests/widget/open/_conflicts.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_conflicts.html.haml
@@ -1,4 +1,4 @@
-%h4
+%h4.has-conflicts
   = icon("exclamation-triangle")
   This merge request contains merge conflicts
 
diff --git a/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml
index c15faa8044fbcf00bd7ed02fced91e8801b0fbc9..fa6149cd3fa2cb06926d7c1b340668904a2ac13f 100644
--- a/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml
@@ -19,7 +19,7 @@
   - if remove_source_branch_button || user_can_cancel_automatic_merge
     .clearfix.prepend-top-10
       - if remove_source_branch_button
-        = link_to merge_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request, merge_when_build_succeeds: true, should_remove_source_branch: true), remote: true, method: :post, class: "btn btn-grouped btn-primary btn-sm remove_source_branch" do
+        = link_to merge_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request, merge_when_build_succeeds: true, should_remove_source_branch: true, sha: @merge_request.source_sha), remote: true, method: :post, class: "btn btn-grouped btn-primary btn-sm remove_source_branch" do
           = icon('times')
           Remove Source Branch When Merged
 
diff --git a/app/views/projects/merge_requests/widget/open/_sha_mismatch.html.haml b/app/views/projects/merge_requests/widget/open/_sha_mismatch.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..499624f8dd8cce93bbf8247b282565fa1149c278
--- /dev/null
+++ b/app/views/projects/merge_requests/widget/open/_sha_mismatch.html.haml
@@ -0,0 +1,6 @@
+%h4
+  = icon("exclamation-triangle")
+  This merge request has received new commits since the page was loaded.
+
+%p
+  Please reload the page to review the new commits before merging.
diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index 687222fa92f3a356e3a29b6a148f3d9a3fc323b8..cbf1ba04170c48e218c56164ac9c7bb9ca807b1d 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_form.html.haml
@@ -17,9 +17,9 @@
     .col-md-6
       .form-group
         = f.label :due_date, "Due Date", class: "control-label"
-        .col-sm-10= f.hidden_field :due_date
         .col-sm-10
-          .datepicker
+          = f.text_field :due_date, class: "datepicker form-control", placeholder: "Select due date"
+          %a.inline.prepend-top-5.js-clear-due-date{ href: "#" } Clear due date
 
   .form-actions
     - if @milestone.new_record?
@@ -28,10 +28,3 @@
     -else
       = f.submit 'Save changes', class: "btn-save btn"
       = link_to "Cancel", namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-cancel"
-
-
-:javascript
-  $(".datepicker").datepicker({
-    dateFormat: "yy-mm-dd",
-    onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) }
-  }).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', $('#milestone_due_date').val()));
diff --git a/app/views/projects/milestones/_header_title.html.haml b/app/views/projects/milestones/_header_title.html.haml
deleted file mode 100644
index 5f4b6982a6de119ea3b5c7b814996827a28df438..0000000000000000000000000000000000000000
--- a/app/views/projects/milestones/_header_title.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-- header_title project_title(@project, "Milestones", namespace_project_milestones_path(@project.namespace, @project))
diff --git a/app/views/projects/milestones/edit.html.haml b/app/views/projects/milestones/edit.html.haml
index 43f8863163dadbd50cbdc1603e899c9a2e4c6e17..be682226ab68e2b21436b8399d86cc8a88d2a78a 100644
--- a/app/views/projects/milestones/edit.html.haml
+++ b/app/views/projects/milestones/edit.html.haml
@@ -1,5 +1,4 @@
 - page_title "Edit", @milestone.title, "Milestones"
-= render "header_title"
 
 %h3.page-title
   Edit Milestone ##{@milestone.iid}
diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml
index abe567af1dd92d3fa10f42dab1292dd5acd8af40..b0e0bdfff5ad60ca8d6eb8b7ee2e1dd00ae72321 100644
--- a/app/views/projects/milestones/index.html.haml
+++ b/app/views/projects/milestones/index.html.haml
@@ -1,22 +1,22 @@
+- @no_container = true
 - page_title "Milestones"
-= render "header_title"
+= render "projects/issues/head"
 
+%div{ class: (container_class) }
+  .top-area
+    = render 'shared/milestones_filter'
 
-.top-area
-  = render 'shared/milestones_filter'
+    .nav-controls
+      - if can?(current_user, :admin_milestone, @project)
+        = link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "btn btn-new", title: "New Milestone" do
+          New Milestone
 
-  .nav-controls
-    - if can?(current_user, :admin_milestone, @project)
-      = link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "btn btn-new", title: "New Milestone" do
-        = icon('plus')
-        New Milestone
+  .milestones
+    %ul.content-list
+      = render @milestones
 
-.milestones
-  %ul.content-list
-    = render @milestones
+      - if @milestones.blank?
+        %li
+          .nothing-here-block No milestones to show
 
-    - if @milestones.blank?
-      %li
-        .nothing-here-block No milestones to show
-
-  = paginate @milestones, theme: "gitlab"
+    = paginate @milestones, theme: "gitlab"
diff --git a/app/views/projects/milestones/new.html.haml b/app/views/projects/milestones/new.html.haml
index 0d016f7831391f3f3a5d457679f7b275063fb0a6..7f372b41698bd4f865e5bc2aeb9ea406528308a4 100644
--- a/app/views/projects/milestones/new.html.haml
+++ b/app/views/projects/milestones/new.html.haml
@@ -1,5 +1,4 @@
 - page_title "New Milestone"
-= render "header_title"
 
 %h3.page-title
   New Milestone
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index 6ec8466015760617b20e1a35db0adf9bd527a022..73772cc0e323e0cc971513f0b091a0ef8a579332 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -1,14 +1,12 @@
 - page_title       @milestone.title, "Milestones"
 - page_description @milestone.description
 
-= render "header_title"
-
 .detail-page-header
   .status-box{ class: status_box_class(@milestone) }
     - if @milestone.closed?
       Closed
     - elsif @milestone.expired?
-      Expired
+      Past due
     - else
       Open
   %span.identifier
@@ -25,11 +23,9 @@
         = 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 edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped btn-nr" do
-        = icon('pencil-square-o')
         Edit
 
       = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-danger" do
-        = icon('trash-o')
         Delete
 
 .detail-page-description.milestone-detail
diff --git a/app/views/projects/network/_head.html.haml b/app/views/projects/network/_head.html.haml
index c609c505def0213531d37ab884996b79a32a91d9..86295a3d0110034cda2efe4602b1c5a7672b8b33 100644
--- a/app/views/projects/network/_head.html.haml
+++ b/app/views/projects/network/_head.html.haml
@@ -1,6 +1,9 @@
-.row-content-block.append-bottom-default
-  .tree-ref-holder
-    = render partial: 'shared/ref_switcher', locals: {destination: 'graph'}
+- @no_container = true
 
-  .oneline
-    You can move around the graph by using the arrow keys.
+%div{ class: (container_class) }
+  .row-content-block.second-block.content-component-block
+    .tree-ref-holder
+      = render partial: 'shared/ref_switcher', locals: {destination: 'graph'}
+
+    .oneline
+      You can move around the graph by using the arrow keys.
diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml
index 8065663ca2a873740c3ae121399f20be94b2a237..e4ab064eda8ad12242dcfca482f0b9296e39847c 100644
--- a/app/views/projects/network/show.html.haml
+++ b/app/views/projects/network/show.html.haml
@@ -1,27 +1,19 @@
 - page_title "Network", @ref
-= render "projects/commits/header_title"
+- page_specific_javascripts asset_path("network/application.js")
 = render "projects/commits/head"
 = render "head"
-.project-network
-  .controls
-    = form_tag namespace_project_network_path(@project.namespace, @project, @id), method: :get, class: 'form-inline network-form' do |f|
-      = text_field_tag :extended_sha1, @options[:extended_sha1], placeholder: "Input an extended SHA1 syntax", class: 'search-input form-control input-mx-250 search-sha'
-      = button_tag class: 'btn btn-success' do
-        = icon('search')
-      .inline.prepend-left-20
-        .checkbox.light
-          = label_tag :filter_ref do
-            = check_box_tag :filter_ref, 1, @options[:filter_ref]
-            %span Begin with the selected commit
+%div{ class: (container_class) }
+  .project-network
+    .controls
+      = form_tag namespace_project_network_path(@project.namespace, @project, @id), method: :get, class: 'form-inline network-form' do |f|
+        = text_field_tag :extended_sha1, @options[:extended_sha1], placeholder: "Input an extended SHA1 syntax", class: 'search-input form-control input-mx-250 search-sha'
+        = button_tag class: 'btn btn-success' do
+          = icon('search')
+        .inline.prepend-left-20
+          .checkbox.light
+            = label_tag :filter_ref do
+              = check_box_tag :filter_ref, 1, @options[:filter_ref]
+              %span Begin with the selected commit
 
-  .network-graph
-    = spinner nil, true
-
-:javascript
-  network_graph = new Network({
-    url: "#{escape_javascript(@url)}",
-    commit_url: "#{escape_javascript(@commit_url)}",
-    ref: "#{escape_javascript(@ref)}",
-    commit_id: '#{@commit.id}'
-  })
-  new ShortcutsNetwork(network_graph.branch_graph)
+    .network-graph{ data: { url: '#{escape_javascript(@url)}', commit_url: '#{escape_javascript(@commit_url)}', ref: '#{escape_javascript(@ref)}', commit_id: '#{escape_javascript(@commit.id)}' } }
+      = spinner nil, true
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index a4c6094c69af0733fe8a566d2d08468009b46510..7e8b8f8346793143c34ad691406d37ffd040a00b 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -1,5 +1,5 @@
 - page_title    'New Project'
-- header_title  "Projects", root_path
+- header_title  "Projects", dashboard_projects_path
 
 %h3.page-title
   New Project
@@ -11,26 +11,22 @@
   .project-edit-content
 
     = form_for @project, html: { class: 'new_project form-horizontal js-requires-input' } do |f|
-      .form-group.project-name-holder
+      .form-group
         = f.label :path, class: 'control-label' do
-          Project path
+          Project owner
         .col-sm-10
-          .input-group
-            - if current_user.can_select_namespace?
-              .input-group-addon
-                = root_url
-              = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user, display_path: true), {}, {class: 'select2 js-select-namespace', tabindex: 1}
-              .input-group-addon
-                \/
-            - else
-              .input-group-addon
-                #{root_url}#{current_user.username}/
-            = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true
-
+          = f.select :namespace_id, namespaces_options(:current_user), {}, {class: 'select2 js-select-namespace', tabindex: 1}
+        
           - if current_user.can_create_group?
             .help-block
               Want to house several dependent projects under the same namespace?
               = link_to "Create a group", new_group_path
+              
+      .form-group
+        = f.label :path, class: 'control-label' do
+          Project name
+        .col-sm-10
+          = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true
 
       - if import_sources_enabled?
         .project-import.js-toggle-container
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index 9fbc9a45549125d86e0fc4c4daee57f6c0ed8669..bcdbff080116aea18b6dff84d739766a1f732eb2 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -19,20 +19,25 @@
         .note-actions
           - access = note.project.team.human_max_access(note.author.id)
           - if access
-            %span.note-role
-              = access
+            %span.note-role.hidden-xs= access
+          - if current_user
+            = link_to '#', title: 'Award Emoji', class: 'note-action-button note-emoji-button js-add-award js-note-emoji', data: { position: 'right' } do
+              = icon('spinner spin')
+              = icon('smile-o')
           - if note_editable
             = link_to '#', title: 'Edit comment', class: 'note-action-button js-note-edit' do
               = icon('pencil')
-            = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button js-note-delete danger' do
+            = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button hidden-xs js-note-delete danger' do
               = icon('trash-o')
       .note-body{class: note_editable ? 'js-task-list-container' : ''}
         .note-text
           = preserve do
-            = markdown(note.note, pipeline: :note, cache_key: [note, "note"])
+            = markdown(note.note, pipeline: :note, cache_key: [note, "note"], author: note.author)
+          = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true)
         - if note_editable
           = render 'projects/notes/edit_form', note: note
-      = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true)
+        .note-awards
+          = render 'award_emoji/awards_block', awardable: note, inline: false
 
       - if note.attachment.url
         .note-attachment
diff --git a/app/views/projects/path_locks/_path_lock.html.haml b/app/views/projects/path_locks/_path_lock.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..d118169216d1f2f411688bdea7804653f4bf2fd5
--- /dev/null
+++ b/app/views/projects/path_locks/_path_lock.html.haml
@@ -0,0 +1,12 @@
+%li
+  %div
+    %span.item-title
+      = icon('lock')
+      = path_lock.path
+
+    .controls
+      - if can_unlock?(path_lock)
+        = link_to namespace_project_path_lock_path(@project.namespace, @project, path_lock), class: 'btn btn-grouped btn-xs btn-remove remove-row has-tooltip', title: "Unlock", method: :delete, data: { confirm: "Are you sure you want to unlock #{path_lock.path}?", container: 'body' }, remote: true do
+          = icon("trash-o")
+
+  locked by #{path_lock.user.name} #{time_ago_with_tooltip(path_lock.created_at)}
diff --git a/app/views/projects/path_locks/destroy.js.haml b/app/views/projects/path_locks/destroy.js.haml
new file mode 100644
index 0000000000000000000000000000000000000000..22f491d0b2804b8f5cd8b8be191da5635b06179b
--- /dev/null
+++ b/app/views/projects/path_locks/destroy.js.haml
@@ -0,0 +1,2 @@
+- unless @project.path_locks.any?
+  $('.locks').load(document.URL + ' .nothing-here-block').hide().fadeIn(1000)
diff --git a/app/views/projects/path_locks/index.html.haml b/app/views/projects/path_locks/index.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..1c4e8cfa814f914a831806301400be1ec5de95c9
--- /dev/null
+++ b/app/views/projects/path_locks/index.html.haml
@@ -0,0 +1,17 @@
+- page_title "File Locks"
+= render "projects/commits/head"
+
+%div{ class: (container_class) }
+  .top-area
+    .nav-text
+      Locks give the ability to lock specific file or folder.
+
+  .locks
+    - if @path_locks.any?
+      %ul.content-list
+        = render @path_locks
+
+      = paginate @path_locks, theme: 'gitlab'
+    - else
+      .nothing-here-block
+        Repository has no locks.
diff --git a/app/views/projects/pipelines/_head.html.haml b/app/views/projects/pipelines/_head.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..d65faf86d4ed4cb8c672ad1020356cc6e2ea4b96
--- /dev/null
+++ b/app/views/projects/pipelines/_head.html.haml
@@ -0,0 +1,19 @@
+.nav-links.sub-nav
+  %ul{ class: (container_class) }
+    - if project_nav_tab? :pipelines
+      = nav_link(controller: :pipelines) do
+        = link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
+          %span
+            Pipelines
+
+    - if project_nav_tab? :builds
+      = nav_link(controller: %w(builds)) do
+        = link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do
+          %span
+            Builds
+
+    - if project_nav_tab? :environments
+      = nav_link(controller: %w(environments)) do
+        = link_to project_environments_path(@project), title: 'Environments', class: 'shortcuts-environments' do
+          %span
+            Environments
diff --git a/app/views/projects/pipelines/_header_title.html.haml b/app/views/projects/pipelines/_header_title.html.haml
deleted file mode 100644
index faf63d64a792a319728365034490699252f20192..0000000000000000000000000000000000000000
--- a/app/views/projects/pipelines/_header_title.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-- header_title project_title(@project, "Pipelines", project_pipelines_path(@project))
diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml
index 9d5b6d367c9f115172f48a89245b5b0d7ff1158b..b70693eeb62923d748691fe1b7398342ac623d7d 100644
--- a/app/views/projects/pipelines/index.html.haml
+++ b/app/views/projects/pipelines/index.html.haml
@@ -1,66 +1,58 @@
+- @no_container = true
 - page_title "Pipelines"
-= render "header_title"
-
-.top-area
-  %ul.nav-links
-    %li{class: ('active' if @scope.nil?)}
-      = link_to project_pipelines_path(@project) do
-        All
-        %span.badge.js-totalbuilds-count
-          = number_with_delimiter(@pipelines_count)
-
-    %li{class: ('active' if @scope == 'running')}
-      = link_to project_pipelines_path(@project, scope: :running) do
-        Running
-        %span.badge.js-running-count
-          = number_with_delimiter(@running_or_pending_count)
-
-    %li{class: ('active' if @scope == 'branches')}
-      = link_to project_pipelines_path(@project, scope: :branches) do
-        Branches
-
-    %li{class: ('active' if @scope == 'tags')}
-      = link_to project_pipelines_path(@project, scope: :tags) do
-        Tags
-
-  .nav-controls
-    - if can? current_user, :create_pipeline, @project
-      = link_to new_namespace_project_pipeline_path(@project.namespace, @project), class: 'btn btn-create' do
-        = icon('plus')
-        New pipeline
-
-      - unless @repository.gitlab_ci_yml
-        = link_to 'Get started with Pipelines', help_page_path('ci/quick_start', 'README'), class: 'btn btn-info'
-
-      = link_to ci_lint_path, class: 'btn btn-default' do
-        = icon('wrench')
-        %span CI Lint
-
-.row-content-block
-  - if @scope == 'running'
-    Running pipelines for this project
-  - elsif @scope.nil?
-    Pipelines for this project
-  - else
-    #{@scope.titleize} for this project
-
-%ul.content-list
-  - stages = @pipelines.stages
-  - if @pipelines.blank?
-    %li
-      .nothing-here-block No pipelines to show
-  - else
-    .table-holder
-      %table.table.builds
-        %tbody
-          %th ID
-          %th Commit
-          - stages.each do |stage|
+= render "projects/pipelines/head"
+
+%div{ class: (container_class) }
+  .top-area
+    %ul.nav-links
+      %li{class: ('active' if @scope.nil?)}
+        = link_to project_pipelines_path(@project) do
+          All
+          %span.badge.js-totalbuilds-count
+            = number_with_delimiter(@pipelines_count)
+
+      %li{class: ('active' if @scope == 'running')}
+        = link_to project_pipelines_path(@project, scope: :running) do
+          Running
+          %span.badge.js-running-count
+            = number_with_delimiter(@running_or_pending_count)
+
+      %li{class: ('active' if @scope == 'branches')}
+        = link_to project_pipelines_path(@project, scope: :branches) do
+          Branches
+
+      %li{class: ('active' if @scope == 'tags')}
+        = link_to project_pipelines_path(@project, scope: :tags) do
+          Tags
+
+    .nav-controls
+      - if can? current_user, :create_pipeline, @project
+        = link_to new_namespace_project_pipeline_path(@project.namespace, @project), class: 'btn btn-create' do
+          New pipeline
+
+        - unless @repository.gitlab_ci_yml
+          = link_to 'Get started with Pipelines', help_page_path('ci/quick_start', 'README'), class: 'btn btn-info'
+
+        = link_to ci_lint_path, class: 'btn btn-default' do
+          %span CI Lint
+
+  %ul.content-list.pipelines
+    - stages = @pipelines.stages
+    - if @pipelines.blank?
+      %li
+        .nothing-here-block No pipelines to show
+    - else
+      .table-holder
+        %table.table.builds
+          %tbody
+            %th ID
+            %th Commit
+            - stages.each do |stage|
+              %th.stage
+                %span.has-tooltip{ title: "#{stage.titleize}" }
+                  = stage.titleize.pluralize
+            %th Duration
             %th
-              %span.pipeline-stage.has-tooltip{ title: "#{stage.titleize}" }
-                = stage.titleize.pluralize
-          %th
-          %th
-        = render @pipelines, commit_sha: true, stage: true, allow_retry: true, stages: stages
+          = render @pipelines, commit_sha: true, stage: true, allow_retry: true, stages: stages
 
-    = paginate @pipelines, theme: 'gitlab'
+      = paginate @pipelines, theme: 'gitlab'
diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml
index b97c9f5f3b6e8f1663a231b2f66053361fd3c88a..5f4ec2e40c85e9c510aeebe395a735f4960007cb 100644
--- a/app/views/projects/pipelines/new.html.haml
+++ b/app/views/projects/pipelines/new.html.haml
@@ -1,5 +1,4 @@
 - page_title "New Pipeline"
-= render "header_title"
 
 %h3.page-title
   New Pipeline
diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml
index b082d4d5da8249cf25370c8fa1a6213c27114294..75943c64276bfd4b0318990caa700c0d46c2daeb 100644
--- a/app/views/projects/pipelines/show.html.haml
+++ b/app/views/projects/pipelines/show.html.haml
@@ -1,9 +1,8 @@
 - page_title "Pipeline"
 
-= render "header_title"
 .prepend-top-default
   - if @commit
     = render "projects/pipelines/info"
   %div.block-connector
 
-= render "projects/commit/ci_commit", ci_commit: @pipeline
+= render "projects/commit/pipeline", pipeline: @pipeline
diff --git a/app/views/projects/project_members/_group_members.html.haml b/app/views/projects/project_members/_group_members.html.haml
index c53033e367c8f2f743764f1323b92487f511ade5..cb6136c215a9158f1eb1355e998609bc039474fe 100644
--- a/app/views/projects/project_members/_group_members.html.haml
+++ b/app/views/projects/project_members/_group_members.html.haml
@@ -6,12 +6,14 @@
       (#{members.count})
     - if can?(current_user, :admin_group_member, @group)
       .controls
-        = link_to group_group_members_path(@group), class: 'btn' do
-          = icon('pencil-square-o')
-          Manage group members
+        = link_to 'Manage group members',
+                  group_group_members_path(@group),
+                  class: 'btn'
   %ul.content-list
-    - members.limit(20).each do |member|
-      = render 'groups/group_members/group_member', member: member, show_controls: false
-    - if members.count > 20
+    = render partial: 'shared/members/member',
+             collection: members.limit(20),
+             as: :member,
+             locals: { show_controls: false }
+    - if members.size > 20
       %li
         and #{members.count - 20} more. For full list visit #{link_to 'group members page', group_group_members_path(@group)}
diff --git a/app/views/projects/project_members/_header_title.html.haml b/app/views/projects/project_members/_header_title.html.haml
deleted file mode 100644
index a31f0a37fa2a42e667291ca64251a900040c3f8a..0000000000000000000000000000000000000000
--- a/app/views/projects/project_members/_header_title.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-- header_title project_title(@project, "Members", namespace_project_project_members_path(@project.namespace, @project))
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 f0f3bb3c177eab2d04f5b1f01ef0135f4b6c4fb2..82892a3335828e877ba7510598c6fe34bca68e4d 100644
--- a/app/views/projects/project_members/_new_project_member.html.haml
+++ b/app/views/projects/project_members/_new_project_member.html.haml
@@ -9,7 +9,7 @@
   .form-group
     = f.label :access_level, "Project Access", class: 'control-label'
     .col-sm-10
-      = select_tag :access_level, options_for_select(ProjectMember.access_roles, @project_member.access_level), class: "project-access-select select2"
+      = select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "project-access-select select2"
       .help-block
         Read more about role permissions
         %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
diff --git a/app/views/projects/project_members/_project_member.html.haml b/app/views/projects/project_members/_project_member.html.haml
deleted file mode 100644
index 05bf3a7ef6a12ee2a2265ba519ea32b4e636e9f3..0000000000000000000000000000000000000000
--- a/app/views/projects/project_members/_project_member.html.haml
+++ /dev/null
@@ -1,55 +0,0 @@
-- user = member.user
-- return unless user || member.invite?
-
-%li{class: "#{dom_class(member)} js-toggle-container project_member_row access-#{member.human_access.downcase}", id: dom_id(member)}
-  %span.list-item-name
-    - if member.user
-      = image_tag avatar_icon(user, 24), class: "avatar s24", alt: ''
-      %strong
-        = link_to user.name, user_path(user)
-      %span.cgray= user.username
-      - if user == current_user
-        %span.label.label-success It's you
-      - if user.blocked?
-        %label.label.label-danger
-          %strong Blocked
-    - else
-      = image_tag avatar_icon(member.invite_email, 24), class: "avatar s24", alt: ''
-      %strong
-        = member.invite_email
-      %span.cgray
-        invited
-        - if member.created_by
-          by
-          = link_to member.created_by.name, user_path(member.created_by)
-        = time_ago_with_tooltip(member.created_at)
-
-      - if can?(current_user, :admin_project_member, @project)
-        = link_to resend_invite_namespace_project_project_member_path(@project.namespace, @project, member), method: :post, class: "btn-xs btn", title: 'Resend invite' do
-          Resend invite
-
-  - if can?(current_user, :admin_project_member, @project)
-    .pull-right
-      %strong= member.human_access
-      - if can?(current_user, :update_project_member, member)
-        = button_tag class: "btn-xs btn js-toggle-button",
-                     title: 'Edit access level', type: 'button' do
-          %i.fa.fa-pencil-square-o
-
-      - if can?(current_user, :destroy_project_member, member)
-        &nbsp;
-        - if current_user == user
-          = link_to leave_namespace_project_project_members_path(@project.namespace, @project), data: { confirm: leave_project_message(@project) }, method: :delete, class: "btn-xs btn btn-remove", title: 'Leave project' do
-            = icon("sign-out")
-            Leave
-        - else
-          = link_to namespace_project_project_member_path(@project.namespace, @project, member), data: { confirm: remove_from_project_team_message(@project, member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from team' do
-            %i.fa.fa-minus.fa-inverse
-
-    .edit-member.hide.js-toggle-content
-      %br
-      = form_for member, as: :project_member, url: namespace_project_project_member_path(@project.namespace, @project, member), remote: true do |f|
-        .prepend-top-10
-          = f.select :access_level, options_for_select(ProjectMember.access_roles, member.access_level), {}, class: 'form-control'
-        .prepend-top-10
-          = f.submit 'Save', class: 'btn btn-save'
diff --git a/app/views/projects/project_members/_shared_group_members.html.haml b/app/views/projects/project_members/_shared_group_members.html.haml
index ae13f8428f0e1a89a9d1d8165b1b9d21ce528dfd..952844acefc3997e195d30ae05f869199a4f840a 100644
--- a/app/views/projects/project_members/_shared_group_members.html.haml
+++ b/app/views/projects/project_members/_shared_group_members.html.haml
@@ -14,8 +14,10 @@
             %i.fa.fa-pencil-square-o
             Edit group members
     %ul.content-list
-      - shared_group.group_members.order('access_level DESC').limit(20).each do |member|
-        = render 'groups/group_members/group_member', member: member, show_controls: false, show_roles: false
+      = render partial: 'shared/members/member',
+               collection: shared_group.group_members.order(access_level: :desc).limit(20),
+               as: :member,
+               locals: { show_controls: false, show_roles: false }
       - if shared_group_users_count > 20
         %li
           and #{shared_group_users_count - 20} more. For full list visit #{link_to 'group members page', group_group_members_path(shared_group)}
diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml
index e8dce30425f13d48bad944618d5abb6046fb626e..03207614258b08c3f914c09cb7ebd5c9f382e168 100644
--- a/app/views/projects/project_members/_team.html.haml
+++ b/app/views/projects/project_members/_team.html.haml
@@ -11,8 +11,7 @@
         = button_tag class: 'btn', title: 'Search' do
           = icon("search")
   %ul.content-list
-    - members.each do |project_member|
-      = render 'project_member', member: project_member
+    = render partial: 'shared/members/member', collection: members, as: :member
 
 :javascript
   $('form.member-search-form').on('submit', function (event) {
diff --git a/app/views/projects/project_members/import.html.haml b/app/views/projects/project_members/import.html.haml
index 189906498cbdfd02d55ad240a4226c1d48e3fb7a..eef97107d7710db73515b0493e0d32cb2a2f95d8 100644
--- a/app/views/projects/project_members/import.html.haml
+++ b/app/views/projects/project_members/import.html.haml
@@ -1,5 +1,4 @@
 - page_title "Import members"
-= render "header_title"
 
 %h3.page-title
   Import members from another project
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index b6d2af5100eedb338a80f4cb2ff78b835b2c66bf..9bad9822ff8a3a4b02a598b6f24c6c6d6e7a3696 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -1,5 +1,4 @@
 - page_title "Members"
-= render "header_title"
 
 - if membership_locked?
   .alert.alert-warning
@@ -18,7 +17,9 @@
           Users with access to this project are listed below.
         = render "new_project_member"
 
-  = render "team", members: @project_members
+    = render 'shared/members/requests', membership_source: @project, members: @project_members.request
+
+  = render 'team', members: @project_members.non_request
 
   - if @group
     = render "group_members", members: @group_members
diff --git a/app/views/projects/project_members/update.js.haml b/app/views/projects/project_members/update.js.haml
index 2fb3a41d541051ba1fa06df53f611abb0e60b4c5..45f8ef890606e95ce6e022b3b00664d4b2921351 100644
--- a/app/views/projects/project_members/update.js.haml
+++ b/app/views/projects/project_members/update.js.haml
@@ -1,2 +1,2 @@
 :plain
-  $("##{dom_id(@project_member)}").replaceWith('#{escape_javascript(render("project_member", member: @project_member))}');
+  $("##{dom_id(@project_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @project_member))}');
diff --git a/app/views/projects/refs/logs_tree.js.haml b/app/views/projects/refs/logs_tree.js.haml
index 8ee2aef0e61d60815e2334eafd6dba52d04244f7..863a4feae279d8987737f0ec9137b50c44cfccd5 100644
--- a/app/views/projects/refs/logs_tree.js.haml
+++ b/app/views/projects/refs/logs_tree.js.haml
@@ -1,6 +1,7 @@
 - @logs.each do |content_data|
   - file_name = content_data[:file_name]
   - commit = content_data[:commit]
+  - path_lock_info = content_data[:path_lock_info]
   - next unless commit
 
   :plain
@@ -8,6 +9,14 @@
     row.find("td.tree_time_ago").html('#{escape_javascript time_ago_with_tooltip(commit.committed_date)}');
     row.find("td.tree_commit").html('#{escape_javascript render("projects/tree/tree_commit_column", commit: commit)}');
 
+  - if path_lock_info
+    :plain
+      var label = $("<span>")
+        .attr('title', 'Locked by #{escape_javascript path_lock_info.user.name }')
+        .attr('data-toggle', 'tooltip')
+        .addClass('fa fa-lock prepend-left-5');
+      row.find('td.tree-item-file-name').append(label);
+
 - if @more_log_url
   :plain
     if($('#tree-slider').length) {
diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml
index 0d59cec322ca9b966df27de658e7882c4defdd34..835398b6f9895ca0ad4251664ecc67807955d5a9 100644
--- a/app/views/projects/releases/edit.html.haml
+++ b/app/views/projects/releases/edit.html.haml
@@ -1,5 +1,4 @@
 - page_title "Edit", @tag.name, "Tags"
-= render "projects/commits/header_title"
 = render "projects/commits/head"
 
 .row-content-block
diff --git a/app/views/projects/repositories/_feed.html.haml b/app/views/projects/repositories/_feed.html.haml
index 6ca919f7f80292ad448a95b606e63a783a88fbc8..43a6fdfd103c7cb3486b4701a592fbea324c9ca8 100644
--- a/app/views/projects/repositories/_feed.html.haml
+++ b/app/views/projects/repositories/_feed.html.haml
@@ -12,7 +12,7 @@
       = link_to namespace_project_commits_path(@project.namespace, @project, commit.id) do
         %code= commit.short_id
       = image_tag avatar_icon(commit.author_email), class: "", width: 16, alt: ''
-      = markdown escape_once(truncate(commit.title, length: 40)), pipeline: :single_line
+      = markdown escape_once(truncate(commit.title, length: 40)), pipeline: :single_line, author: commit.author
   %td
     %span.pull-right.cgray
       = time_ago_with_tooltip(commit.committed_date)
diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml
index 30cd1263a12235a77573f78c6a96bccd1036ebf4..8ae9f0d95f7f7c6bccf59b19a4d83b20570c5d69 100644
--- a/app/views/projects/runners/_specific_runners.html.haml
+++ b/app/views/projects/runners/_specific_runners.html.haml
@@ -8,7 +8,7 @@
       Install GitLab Runner software.
       Checkout the #{link_to 'GitLab Runner section', 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'} to install it
     %li
-      Specify following URL during runner setup:
+      Specify the following URL during runner setup:
       %code #{ci_root_url(only_path: false)}
     %li
       Use the following registration token during setup:
diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml
index 1b70880043a85f167188d6fdcf496b493416b707..1f13ea28b4e14243597fbe9517e3e2ad2c35c9fb 100644
--- a/app/views/projects/services/_form.html.haml
+++ b/app/views/projects/services/_form.html.haml
@@ -1,18 +1,16 @@
-%h3.page-title
-  = @service.title
-  = boolean_to_icon @service.activated?
+.row.prepend-top-default.append-bottom-default
+  .col-lg-3
+    %h4.prepend-top-0
+      = @service.title
+      = boolean_to_icon @service.activated?
 
-%p= @service.description
-
-%hr
-
-= form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |form|
-  = render 'shared/service_settings', form: form
-
-  .form-actions
-    = form.submit 'Save changes', class: 'btn btn-save'
-    &nbsp;
-    - if @service.valid? && @service.activated?
-      - disabled = @service.can_test? ? '':'disabled'
-      = link_to 'Test settings', test_namespace_project_service_path(@project.namespace, @project, @service.to_param), class: "btn #{disabled}"
-    = link_to "Cancel", namespace_project_services_path(@project.namespace, @project), class: "btn btn-cancel"
+    %p= @service.description
+  .col-lg-9
+    = form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |form|
+      = render 'shared/service_settings', form: form
+      = form.submit 'Save changes', class: 'btn btn-save'
+      &nbsp;
+      - if @service.valid? && @service.activated?
+        - disabled = @service.can_test? ? '':'disabled'
+        = link_to 'Test settings', test_namespace_project_service_path(@project.namespace, @project, @service.to_param), class: "btn #{disabled}"
+      = link_to "Cancel", namespace_project_services_path(@project.namespace, @project), class: "btn btn-cancel"
diff --git a/app/views/projects/services/index.html.haml b/app/views/projects/services/index.html.haml
index c1356f6db02dafca076fc28956a982e22dfdb1e7..4a33a5bc6f6b8a67d59504a854c2f60c8a83c1b9 100644
--- a/app/views/projects/services/index.html.haml
+++ b/app/views/projects/services/index.html.haml
@@ -1,24 +1,32 @@
 - page_title "Services"
-%h3.page-title Project services
-%p.light Project services allow you to integrate GitLab with other applications
 
-.table-holder
-  %table.table
-    %thead
-      %tr
-        %th
-        %th Service
-        %th Description
-        %th Last edit
-    - @services.sort_by(&:title).each do |service|
-      %tr
-        %td
-          = boolean_to_icon service.activated?
-        %td
-          = link_to edit_namespace_project_service_path(@project.namespace, @project, service.to_param) do
-            %strong= service.title
-        %td
-          = service.description
-        %td.light
-          = time_ago_in_words service.updated_at
-          ago
+.row.prepend-top-default.append-bottom-default
+  .col-lg-3
+    %h4.prepend-top-0
+      Project services
+    %p Project services allow you to integrate GitLab with other applications
+  .col-lg-9
+    %table.table
+      %colgroup
+        %col
+        %col
+        %col.hidden-xs
+        %col{ width: "120" }
+      %thead
+        %tr
+          %th
+          %th Service
+          %th.hidden-xs Description
+          %th Last edit
+      - @services.sort_by(&:title).each do |service|
+        %tr
+          %td
+            = boolean_to_icon service.activated?
+          %td
+            = link_to edit_namespace_project_service_path(@project.namespace, @project, service.to_param) do
+              %strong= service.title
+          %td.hidden-xs
+            = service.description
+          %td.light
+            = time_ago_in_words service.updated_at
+            ago
diff --git a/app/views/projects/show.atom.builder b/app/views/projects/show.atom.builder
index 9b3d3f069d9bf9c2b8fdba1018d68af81dd970cb..11310d5e1e1f51cdc956bf965558eec40921460c 100644
--- a/app/views/projects/show.atom.builder
+++ b/app/views/projects/show.atom.builder
@@ -6,7 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
   xml.id      namespace_project_url(@project.namespace, @project)
   xml.updated @events[0].updated_at.xmlschema if @events[0]
 
-  @events.each do |event|
-    event_to_atom(xml, event)
-  end
+  xml << render(@events) if @events.any?
 end
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 74feb9e3282db65f5abbd1be9610a01199e53a94..4afa902b4eb1b0c986c06f0b892e3d5ec8314cf9 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -13,50 +13,50 @@
 = render "home_panel"
 
 .project-stats.row-content-block.second-block
-  %ul.nav
-    %li
-      = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
-        = pluralize(number_with_delimiter(@project.commit_count), 'commit')
-    %li
-      = link_to namespace_project_branches_path(@project.namespace, @project) do
-        = pluralize(number_with_delimiter(@repository.branch_names.count), 'branch')
-    %li
-      = link_to namespace_project_tags_path(@project.namespace, @project) do
-        = pluralize(number_with_delimiter(@repository.tag_names.count), 'tag')
-
-    %li
-      = link_to project_files_path(@project) do
-        = repository_size
-
-    - if default_project_view != 'readme' && @repository.readme
+  %div{ class: (container_class) }
+    %ul.nav
       %li
-        = link_to 'Readme', readme_path(@project)
-
-    - if @repository.changelog
+        = link_to project_files_path(@project) do
+          Files (#{repository_size})
       %li
-        = link_to 'Changelog', changelog_path(@project)
-
-    - if @repository.license_blob
+        = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
+          #{'Commit'.pluralize(@project.commit_count)} (#{number_with_delimiter(@project.commit_count)})
       %li
-        = link_to license_short_name(@project), license_path(@project)
-
-    - if @repository.contribution_guide
+        = link_to namespace_project_branches_path(@project.namespace, @project) do
+          #{'Branch'.pluralize(@repository.branch_names.count)} (#{number_with_delimiter(@repository.branch_names.count)})
       %li
-        = link_to 'Contribution guide', contribution_guide_path(@project)
+        = link_to namespace_project_tags_path(@project.namespace, @project) do
+          #{'Tag'.pluralize(@repository.tag_names.count)} (#{number_with_delimiter(@repository.tag_names.count)})
+
+      - if default_project_view != 'readme' && @repository.readme
+        %li
+          = link_to 'Readme', readme_path(@project)
+
+      - if @repository.changelog
+        %li
+          = link_to 'Changelog', changelog_path(@project)
+
+      - if @repository.license_blob
+        %li
+          = link_to license_short_name(@project), license_path(@project)
+
+      - if @repository.contribution_guide
+        %li
+          = link_to 'Contribution guide', contribution_guide_path(@project)
 
-    - if current_user && can_push_branch?(@project, @project.default_branch)
-      - unless @repository.changelog
-        %li.missing
-          = link_to add_special_file_path(@project, file_name: 'CHANGELOG') do
-            Add Changelog
-      - unless @repository.license_blob
-        %li.missing
-          = link_to add_special_file_path(@project, file_name: 'LICENSE') do
-            Add License
-      - unless @repository.contribution_guide
-        %li.missing
-          = link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do
-            Add Contribution guide
+      - if current_user && can_push_branch?(@project, @project.default_branch)
+        - unless @repository.changelog
+          %li.missing
+            = link_to add_special_file_path(@project, file_name: 'CHANGELOG') do
+              Add Changelog
+        - unless @repository.license_blob
+          %li.missing
+            = link_to add_special_file_path(@project, file_name: 'LICENSE') do
+              Add License
+        - unless @repository.contribution_guide
+          %li.missing
+            = link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do
+              Add Contribution guide
 
 - if @repository.commit
   .content-block.second-block.white
diff --git a/app/views/projects/snippets/_actions.html.haml b/app/views/projects/snippets/_actions.html.haml
index 4a51546942204346dc8d591ba3bf4c1ade0b7b8b..bf57beb9d071413bf3fafd4c103635a4de92f91d 100644
--- a/app/views/projects/snippets/_actions.html.haml
+++ b/app/views/projects/snippets/_actions.html.haml
@@ -1,11 +1,27 @@
-= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do
-  = icon('plus')
-  New Snippet
-- if can?(current_user, :admin_project_snippet, @snippet)
-  = link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' do
-    = icon('trash-o')
-    Delete
-- if can?(current_user, :update_project_snippet, @snippet)
-  = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped snippable-edit" do
-    = icon('pencil-square-o')
-    Edit
+.hidden-xs
+  = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped btn-create new-snippet-link', title: "New Snippet" do
+    = icon('plus')
+    New Snippet
+  - if can?(current_user, :update_project_snippet, @snippet)
+    = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped snippable-edit" do
+      Edit
+  - if can?(current_user, :update_project_snippet, @snippet)
+    = link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-warning", title: 'Delete Snippet' do
+      Delete
+.visible-xs-block.dropdown
+  %button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } }
+    Options
+    %span.caret
+  .dropdown-menu.dropdown-menu-full-width
+    %ul
+      %li
+        = link_to new_namespace_project_snippet_path(@project.namespace, @project), title: "New Snippet" do
+          New Snippet
+      - if can?(current_user, :update_project_snippet, @snippet)
+        %li
+          = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet) do
+            Edit
+      - if can?(current_user, :update_project_snippet, @snippet)
+        %li
+          = link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do
+            Delete
diff --git a/app/views/projects/snippets/_header_title.html.haml b/app/views/projects/snippets/_header_title.html.haml
deleted file mode 100644
index 04f0bbe9853e209b35ad476832d1e871a0cc8405..0000000000000000000000000000000000000000
--- a/app/views/projects/snippets/_header_title.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-- header_title project_title(@project, "Snippets", namespace_project_snippets_path(@project.namespace, @project))
diff --git a/app/views/projects/snippets/edit.html.haml b/app/views/projects/snippets/edit.html.haml
index dc3ea1fcf126877f1cacb03974240525c81e002a..216f70f5605240dad7e00a2746d828071e54c685 100644
--- a/app/views/projects/snippets/edit.html.haml
+++ b/app/views/projects/snippets/edit.html.haml
@@ -1,5 +1,4 @@
 - page_title "Edit", @snippet.title, "Snippets"
-= render "header_title"
 
 %h3.page-title
   Edit Snippet
diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml
index 103ff447464e242a9d1292b15ef4b2753edf3030..96fee3b17b215ac4773e4cfa3a9ccfed25c2b8dd 100644
--- a/app/views/projects/snippets/index.html.haml
+++ b/app/views/projects/snippets/index.html.haml
@@ -1,5 +1,4 @@
 - page_title "Snippets"
-= render "header_title"
 
 .row-content-block.top-block
   .pull-right
diff --git a/app/views/projects/snippets/new.html.haml b/app/views/projects/snippets/new.html.haml
index e57237991b44588ba4ba9ec45ad69e9f8ab7e1c9..772a594269ca8bf7e43b1c3e4249c4dad8a6ab36 100644
--- a/app/views/projects/snippets/new.html.haml
+++ b/app/views/projects/snippets/new.html.haml
@@ -1,5 +1,4 @@
 - page_title "New Snippets"
-= render "header_title"
 
 %h3.page-title
   New Snippet
diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml
index 7c599563ce45130f1e76cd7c7f5e8a32c119f83a..bae4d8f349f2c661db9a32d740a5672a60e94017 100644
--- a/app/views/projects/snippets/show.html.haml
+++ b/app/views/projects/snippets/show.html.haml
@@ -1,18 +1,15 @@
 - page_title @snippet.title, "Snippets"
-= render "header_title"
 
 .snippet-holder
   = render 'shared/snippets/header'
 
-  %article.file-holder
-    .file-title
+  %article.file-holder.file-holder-no-border.snippet-file-content
+    .file-title.file-title-clear
       = blob_icon 0, @snippet.file_name
-      %strong
-        = @snippet.file_name
+      = @snippet.file_name
       .file-actions.hidden-xs
         = clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{@snippet.id}']")
         = link_to 'Raw', raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank"
-
     = render 'shared/snippets/blob'
 
   %div#notes= render "projects/notes/notes_with_form"
diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml
index 093d1d1bb0f32ac7d58bf77b85b77bc80cc18ca4..8a11dbfa9f4adfc4f4a9da1229ee8f31c5db155c 100644
--- a/app/views/projects/tags/_download.html.haml
+++ b/app/views/projects/tags/_download.html.haml
@@ -1,7 +1,6 @@
-%span.btn-group.btn-grouped
+%span.btn-group
   = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), class: 'btn btn-default', rel: 'nofollow' do
-    %i.fa.fa-download
-    %span source code
+    %span Source code
   %a.btn.btn-default.dropdown-toggle{ 'data-toggle' => 'dropdown' }
     %span.caret
     %span.sr-only
@@ -9,9 +8,7 @@
   %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' }
     %li
       = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), rel: 'nofollow' do
-        %i.fa.fa-download
         %span Download zip
     %li
       = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do
-        %i.fa.fa-download
         %span Download tar.gz
diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml
index dbc35c16febac6f9b7b942f560f1a0bbfa69fa7e..844e105581064b3b38ec9bd9a2ef28b8632162d2 100644
--- a/app/views/projects/tags/_tag.html.haml
+++ b/app/views/projects/tags/_tag.html.haml
@@ -15,11 +15,11 @@
         = render 'projects/tags/download', ref: tag.name, project: @project
 
       - if can?(current_user, :push_code, @project)
-        = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn has-tooltip', title: "Edit release notes" do
+        = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn has-tooltip', title: "Edit release notes" do
           = icon("pencil")
 
       - if can?(current_user, :admin_project, @project)
-        = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do
+        = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do
           = icon("trash-o")
 
   - if commit
diff --git a/app/views/projects/tags/destroy.js.haml b/app/views/projects/tags/destroy.js.haml
index ffeacb5a0043b8852c85d8d7b7bcd3fd22cf0495..e4a78fadbebb6b005b85337652d6cdfd0631dd4a 100644
--- a/app/views/projects/tags/destroy.js.haml
+++ b/app/views/projects/tags/destroy.js.haml
@@ -1,3 +1,2 @@
-$('.js-totaltags-count').html("#{@repository.tags.size}");
 - if @repository.tags.empty?
   $('.tags').load(document.URL + ' .nothing-here-block').hide().fadeIn(1000)
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index bb6c050965bbb7f82c33541876257d4528929be6..81040437d00468296d00905269b91e6627d01e70 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -1,31 +1,32 @@
+- @no_container = true
 - page_title "Tags"
-= render "projects/commits/header_title"
 = render "projects/commits/head"
 
-.row-content-block
-  - if can? current_user, :push_code, @project
-    .pull-right
-      = link_to new_namespace_project_tag_path(@project.namespace, @project), class: 'btn btn-create new-tag-btn' do
-        = icon('plus')
-        New tag
-  .oneline
-    Tags give the ability to mark specific points in history as being important
+%div{ class: (container_class) }
+  .top-area
+    .nav-text
+      Tags give the ability to mark specific points in history as being important
 
-= render 'projects/commits/mirror_status'
+    - if can? current_user, :push_code, @project
+      .nav-controls
+        = link_to new_namespace_project_tag_path(@project.namespace, @project), class: 'btn btn-create new-tag-btn' do
+          New tag
 
-.tags
-  - unless @tags.empty?
-    %ul.content-list
-      - @tags.each do |tag|
-        = render 'tag', tag: @repository.find_tag(tag)
+  = render 'projects/commits/mirror_status'
 
-    = paginate @tags, theme: 'gitlab'
+  .tags
+    - unless @tags.empty?
+      %ul.content-list
+        - @tags.each do |tag|
+          = render 'tag', tag: @repository.find_tag(tag)
 
-  - else
-    .nothing-here-block
-      Repository has no tags yet.
-      %br
-      %small
-        Use git tag command to add a new one:
+      = paginate @tags, theme: 'gitlab'
+
+    - else
+      .nothing-here-block
+        Repository has no tags yet.
         %br
-        %span.monospace git tag -a v1.4 -m 'version 1.4'
+        %small
+          Use git tag command to add a new one:
+          %br
+          %span.monospace git tag -a v1.4 -m 'version 1.4'
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index f93064532974f551cd62c7b55f3e546c5d343ca4..3a097750d6eeba795e593264014d0be77f0d6ebc 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -1,5 +1,4 @@
 - page_title "New Tag"
-= render "projects/commits/header_title"
 
 - if @error
   .alert.alert-danger
diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml
index 9f1424aecc7bc9d8844a65ae329ff7431dca85d7..b7d7d5c5382da27ecb1d83113ce49797d2c9e0de 100644
--- a/app/views/projects/tags/show.html.haml
+++ b/app/views/projects/tags/show.html.haml
@@ -1,5 +1,4 @@
 - page_title @tag.name, "Tags"
-= render "projects/commits/header_title"
 = render "projects/commits/head"
 
 .row-content-block
diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml
index 558e6146ae993f7c860061ce3223fa21f3dd9149..cf56a9c52e4b75c27ff172a62dc0fbba15f3e364 100644
--- a/app/views/projects/tree/_tree_content.html.haml
+++ b/app/views/projects/tree/_tree_content.html.haml
@@ -15,6 +15,7 @@
                 = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace"
                 &ndash;
                 = truncate(@commit.title, length: 50)
+            = lock_file_link(html_options: {class: 'pull-right prepend-left-10 path-lock'})
             = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'pull-right'
 
       - if @path.present?
@@ -33,6 +34,14 @@
   = render 'projects/blob/upload', title: 'Upload New File', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post
   = render 'projects/blob/new_dir'
 
+- if license_allows_file_locks?
+  :javascript
+    PathLocks.init(
+      '#{toggle_namespace_project_path_locks_path(@project.namespace, @project)}',
+      '#{@path}'
+    );
+
+
 :javascript
   // Load last commit log for each file in tree
   $('#tree-slider').waitForImages(function() {
diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml
index 91fb2a44594760458e0e520b8cc46df9e78b92d4..2abcfcdd7b27d0fe66f56b085674e9a8d2031041 100644
--- a/app/views/projects/tree/show.html.haml
+++ b/app/views/projects/tree/show.html.haml
@@ -1,17 +1,20 @@
+- @no_container = true
+
 - page_title @path.presence || "Files", @ref
-- header_title project_title(@project, "Files", project_files_path(@project))
 = content_for :meta_tags do
   - if current_user
     = 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'
+= render "projects/commits/head"
 
-.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
+%div{ class: (container_class) }
+  .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
-  .nav-block
-    = render 'projects/tree/tree_header', tree: @tree
+  #tree-holder.tree-holder.clearfix
+    .nav-block
+      = render 'projects/tree/tree_header', tree: @tree
 
-  = render 'projects/tree/tree_content', tree: @tree
+    = render 'projects/tree/tree_content', tree: @tree
diff --git a/app/views/projects/triggers/index.html.haml b/app/views/projects/triggers/index.html.haml
index d73ac9871615693c155503e978bfb6810bf1f244..7f3de47d7df4315554ebff11adee6a6bf4700cf5 100644
--- a/app/views/projects/triggers/index.html.haml
+++ b/app/views/projects/triggers/index.html.haml
@@ -5,7 +5,7 @@
     %h4.prepend-top-0
       = page_title
     %p
-      Triggers can be used to force a rebuild of a specific branch or tag with an API call.
+      Triggers can force a specific branch or tag to rebuild with an API call.
   .col-lg-9
     %h5.prepend-top-0
       Your triggers
@@ -19,7 +19,7 @@
           = render partial: 'trigger', collection: @triggers, as: :trigger
     - else
       %p.settings-message.text-center.append-bottom-default
-        There are no triggers to use, add one by the button below.
+        No triggers have been created yet. Add one using the button below.
 
     = form_for @trigger, url: url_for(controller: 'projects/triggers', action: 'create') do |f|
       = f.submit "Add Trigger", class: 'btn btn-success'
@@ -28,8 +28,7 @@
       Use CURL
 
     %p.light
-      Copy the token above and set your branch or tag name. This is the reference that will be rebuild.
-
+      Copy the token above, set your branch or tag name, and that reference will be rebuilt.
 
     %pre
       :plain
@@ -41,10 +40,10 @@
       Use .gitlab-ci.yml
 
     %p.light
-      Copy the snippet to
-      %i .gitlab-ci.yml
-      of dependent project.
-      At the end of your build it will trigger this project to rebuilt.
+      In the
+      %code .gitlab-ci.yml
+      of the dependent project, include the following snippet.
+      The project will rebuild at the end of the build.
 
     %pre
       :plain
@@ -57,9 +56,8 @@
 
     %p.light
       Add
-      %strong variables[VARIABLE]=VALUE
-      to API request.
-      The value of variable could then be used to distinguish triggered build from normal one.
+      %code variables[VARIABLE]=VALUE
+      to an API request. Variable values can be used to distinguish between triggered builds and normal builds.
 
     %pre.append-bottom-0
       :plain
diff --git a/app/views/projects/wikis/_header_title.html.haml b/app/views/projects/wikis/_header_title.html.haml
deleted file mode 100644
index 408adc36ca60bc12407225987387a6d22ab31255..0000000000000000000000000000000000000000
--- a/app/views/projects/wikis/_header_title.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-- header_title project_title(@project, 'Wiki', get_project_wiki_path(@project))
diff --git a/app/views/projects/wikis/_main_links.html.haml b/app/views/projects/wikis/_main_links.html.haml
index 2b91b7e8f65a1cf17ebf2e9dd8070d863bf5ce99..4faa547769b54191430adb92f3d1f45b24066057 100644
--- a/app/views/projects/wikis/_main_links.html.haml
+++ b/app/views/projects/wikis/_main_links.html.haml
@@ -1,11 +1,9 @@
 - if (@page && @page.persisted?)
-  = link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn btn-grouped" do
+  = link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn" do
     Page History
   - if can?(current_user, :create_wiki, @project)
-    = link_to namespace_project_wiki_edit_path(@project.namespace, @project, @page), class: "btn btn-grouped" do
-      %i.fa.fa-pencil-square-o
+    = link_to namespace_project_wiki_edit_path(@project.namespace, @project, @page), class: "btn" do
       Edit
   - if can?(current_user, :admin_wiki, @project)
     = link_to namespace_project_wiki_path(@project.namespace, @project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-remove" do
-      = icon('trash')
       Delete
diff --git a/app/views/projects/wikis/_nav.html.haml b/app/views/projects/wikis/_nav.html.haml
index a722fbc53524a9dc669868398fa31f0ebcfb4a62..988fe024e2877a2d8593d15bb8d324f5e66b3c31 100644
--- a/app/views/projects/wikis/_nav.html.haml
+++ b/app/views/projects/wikis/_nav.html.haml
@@ -13,7 +13,6 @@
   .nav-controls
     - if can?(current_user, :create_wiki, @project)
       = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
-        = icon('plus')
         New Page
 
 = render 'projects/wikis/new'
diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml
index 4dd818c7f6762da3540b2bd71b233a9c99d65d8f..cbd69ee1a73c31d7a46b09f071cac365d75a7008 100644
--- a/app/views/projects/wikis/edit.html.haml
+++ b/app/views/projects/wikis/edit.html.haml
@@ -1,9 +1,8 @@
 - page_title "Edit", @page.title.capitalize, "Wiki"
-= render "header_title"
 = render 'nav'
 
 .top-area
-  .nav-text
+  .nav-text.wiki-page
     %strong
       - if @page.persisted?
         = link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page)
diff --git a/app/views/projects/wikis/empty.html.haml b/app/views/projects/wikis/empty.html.haml
index c7e490c3cd1120696d624717c55e911b28daf81e..7dfa405d0639c93802c617dc71cf8029ec783abb 100644
--- a/app/views/projects/wikis/empty.html.haml
+++ b/app/views/projects/wikis/empty.html.haml
@@ -1,5 +1,4 @@
 - page_title "Wiki"
-= render "header_title"
 
 %h3.page-title Empty page
 %hr
diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml
index ba3f2cadc4811ca0c129ceb2d5ce995b64da2730..ccceab6155e8aaa309ee6e16eac00f0dacf2e110 100644
--- a/app/views/projects/wikis/git_access.html.haml
+++ b/app/views/projects/wikis/git_access.html.haml
@@ -1,5 +1,4 @@
 - page_title "Git Access", "Wiki"
-= render "header_title"
 
 = render 'nav'
 .row-content-block
diff --git a/app/views/projects/wikis/history.html.haml b/app/views/projects/wikis/history.html.haml
index dcaddae2b0412446e626305b19a735a4a3189aa1..45460ed9f41ad2e1769f778dedea40baaed583b9 100644
--- a/app/views/projects/wikis/history.html.haml
+++ b/app/views/projects/wikis/history.html.haml
@@ -1,5 +1,4 @@
 - page_title "History", @page.title.capitalize, "Wiki"
-= render "header_title"
 = render 'nav'
 
 .top-area
diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml
index 92b494a513cad57c0ba11e6a4ec8dae48cb5c4ba..2f6162fa3c5f01d7ccb384e882d1e8c5ef072638 100644
--- a/app/views/projects/wikis/pages.html.haml
+++ b/app/views/projects/wikis/pages.html.haml
@@ -1,5 +1,4 @@
 - page_title "Pages", "Wiki"
-= render "header_title"
 
 = render 'nav'
 
diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml
index 067fb7f8f54782e066f0c679fdf5edf2ddf2c3cc..9166c0edb3b6e5af7e399803e975116f237a414d 100644
--- a/app/views/projects/wikis/show.html.haml
+++ b/app/views/projects/wikis/show.html.haml
@@ -1,5 +1,4 @@
 - page_title @page.title.capitalize, "Wiki"
-= render "header_title"
 = render 'nav'
 
 .top-area
@@ -19,7 +18,7 @@
     You can view the #{link_to "most recent version", namespace_project_wiki_path(@project.namespace, @project, @page)} or browse the #{link_to "history", namespace_project_wiki_history_path(@project.namespace, @project, @page)}.
 
 
-.wiki-holder.prepend-top-default
+.wiki-holder.prepend-top-default.append-bottom-default
   .wiki
     = preserve do
       = render_wiki_content(@page)
diff --git a/app/views/search/results/_issue.html.haml b/app/views/search/results/_issue.html.haml
index 640890fbe928f1bd4aa44d71b2b653d11d9776ec..8f68d6d1b87048d28cbbccc6cb743b9943ef4c64 100644
--- a/app/views/search/results/_issue.html.haml
+++ b/app/views/search/results/_issue.html.haml
@@ -7,7 +7,7 @@
   - if issue.description.present?
     .description.term
       = preserve do
-        = search_md_sanitize(markdown(truncate(issue.description, length: 200, separator: " "), { project: issue.project }))
+        = search_md_sanitize(markdown(truncate(issue.description, length: 200, separator: " "), { project: issue.project, author: issue.author }))
   %span.light
     #{issue.project.name_with_namespace}
   - if issue.closed?
diff --git a/app/views/search/results/_merge_request.html.haml b/app/views/search/results/_merge_request.html.haml
index 333f6533213f6bdfcb37187e7c3c6d5cdaf8edbc..6331c2bd6b044e146756b163cc6e6eda64a8d3b1 100644
--- a/app/views/search/results/_merge_request.html.haml
+++ b/app/views/search/results/_merge_request.html.haml
@@ -6,7 +6,7 @@
   - if merge_request.description.present?
     .description.term
       = preserve do
-        = search_md_sanitize(markdown(merge_request.description, { project: merge_request.project }))
+        = search_md_sanitize(markdown(merge_request.description, { project: merge_request.project, author: merge_request.author }))
   %span.light
     #{merge_request.project.name_with_namespace}
   .pull-right
diff --git a/app/views/search/results/_note.html.haml b/app/views/search/results/_note.html.haml
index d9400b1d9fa63e76c97000c6703c63fb01bca098..8163aff43b67226960864acf3e44cc787ef3d67c 100644
--- a/app/views/search/results/_note.html.haml
+++ b/app/views/search/results/_note.html.haml
@@ -19,4 +19,4 @@
   .note-search-result
     .term
       = preserve do
-        = search_md_sanitize(markdown(note.note, {no_header_anchors: true}))
+        = search_md_sanitize(markdown(note.note, {no_header_anchors: true, author: note.author}))
diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index d47ccc55aa3ba2c6a93bb9b0da5ea2dc3c903d56..d40a2b67b8136c50014fac4454988fe0937510f3 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -5,7 +5,7 @@
     %a#clone-dropdown.clone-dropdown-btn.btn{href: '#', 'data-toggle' => 'dropdown'}
       %span
         = default_clone_protocol.upcase
-      = icon('angle-down')
+      = icon('caret-down')
     %ul.dropdown-menu.dropdown-menu-right.clone-options-dropdown
       %li
         = ssh_clone_button(project)
diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml
index c38d9313dba6e15990e95df594c9714a3b4d9187..300550022130653b035a090fd4faffd7df44340f 100644
--- a/app/views/shared/_event_filter.html.haml
+++ b/app/views/shared/_event_filter.html.haml
@@ -1,5 +1,7 @@
-%ul.nav-links.event-filter
+%ul.nav-links.event-filter.scrolling-tabs
+  .fade-left
   = event_filter_link EventFilter.push, 'Push events'
   = event_filter_link EventFilter.merged, 'Merge events'
   = event_filter_link EventFilter.comments, 'Comments'
   = event_filter_link EventFilter.team, 'Team'
+  .fade-right
diff --git a/app/views/shared/_git_hooks_form.html.haml b/app/views/shared/_git_hooks_form.html.haml
index 543ca9835a2abe62fed9a711b916beccb1c6ae7a..894be74f9436d4b5e3e5fda6b036323ef12629c5 100644
--- a/app/views/shared/_git_hooks_form.html.haml
+++ b/app/views/shared/_git_hooks_form.html.haml
@@ -26,7 +26,7 @@
 
 .form-group
   = f.label :author_email_regex, "Commit author's email", class: 'label-light'
-  = f.text_field :author_email_regex, class: "form-control", placeholder: 'Example: Fixes @my-company.com$'
+  = f.text_field :author_email_regex, class: "form-control", placeholder: 'Example: @my-company.com$'
   .help-block
     All commit author's email must match this
     = link_to 'Ruby regular expression', 'http://www.ruby-doc.org/core-2.1.1/Regexp.html'
diff --git a/app/views/shared/_issues.html.haml b/app/views/shared/_issues.html.haml
index 8ff9d4c1c7f7e620e87fe826cf50c0cd6c69bbc1..a5df502d7b54165c47b8f192754298b041d07485 100644
--- a/app/views/shared/_issues.html.haml
+++ b/app/views/shared/_issues.html.haml
@@ -1,4 +1,4 @@
-- if @issues.any?
+- if @issues.reorder(nil).any?
   - @issues.group_by(&:project).each do |group|
     .panel.panel-default.panel-small
       - project = group[0]
diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml
index 9ce5562e6673b87cbaf5f15eb3d97ee32c8dbcd5..77676454b57f863fa70e97b6543866564cc08301 100644
--- a/app/views/shared/_label_row.html.haml
+++ b/app/views/shared/_label_row.html.haml
@@ -1,5 +1,15 @@
 %span.label-row
+  - if can?(current_user, :admin_label, @project)
+    .draggable-handler
+      = icon('bars')
+    .js-toggle-priority.toggle-priority{ data: { url: remove_priority_namespace_project_label_path(@project.namespace, @project, label),
+      dom_id: dom_id(label) } }
+      %button.add-priority.btn.has-tooltip{ title: 'Prioritize', :'data-placement' => 'top' }
+        = icon('star-o')
+      %button.remove-priority.btn.has-tooltip{ title: 'Remove priority', :'data-placement' => 'top' }
+        = icon('star')
   %span.label-name
     = link_to_label(label, tooltip: false)
-  %span.prepend-left-10
-    = markdown(label.description, pipeline: :single_line)
\ No newline at end of file
+  - if label.description
+    %span.label-description
+      = markdown(label.description, pipeline: :single_line)
diff --git a/app/views/shared/_labels_row.html.haml b/app/views/shared/_labels_row.html.haml
index dc89e36419cd462d539974cf1c4ee155a57dff9c..87028ececd4bf21d4d1ee917c2909b5bff206957 100644
--- a/app/views/shared/_labels_row.html.haml
+++ b/app/views/shared/_labels_row.html.haml
@@ -1,3 +1,10 @@
 - labels.each do |label|
-  %span.label-row
-    = link_to_label(label, tooltip: false)
+  %span.label-row.btn-group{ role: "group", aria: { label: escape_once(label.name) }, style: "color: #{text_color_for_bg(label.color)}" }
+    = link_to namespace_project_label_path(@project.namespace, @project, label),
+      class: "btn btn-transparent has-tooltip",
+      style: "background-color: #{label.color};",
+      title: escape_once(label.description),
+      data: { container: "body" } do
+      = escape_once label.name
+    %button.btn.btn-transparent.label-remove.js-label-filter-remove{ type: "button", style: "background-color: #{label.color};", data: { label: label.title } }
+      = icon("times")
diff --git a/app/views/shared/_merge_requests.html.haml b/app/views/shared/_merge_requests.html.haml
index e74fc36c797c0ba6c0078e5658e18e689f8898be..ca3178395c1508f3a139ae04c9e8a50cc936aac5 100644
--- a/app/views/shared/_merge_requests.html.haml
+++ b/app/views/shared/_merge_requests.html.haml
@@ -1,4 +1,4 @@
-- if @merge_requests.any?
+- if @merge_requests.reorder(nil).any?
   - @merge_requests.group_by(&:target_project).each do |group|
     .panel.panel-default.panel-small
       - project = group[0]
diff --git a/app/views/shared/_new_project_item_select.html.haml b/app/views/shared/_new_project_item_select.html.haml
index 1c58345278a4afbed68b911b9c16fc192780280f..51622931e24c0cf422cc47c4b355e690fcaea11b 100644
--- a/app/views/shared/_new_project_item_select.html.haml
+++ b/app/views/shared/_new_project_item_select.html.haml
@@ -1,8 +1,7 @@
 - if @projects.any?
-  .prepend-left-10.project-item-select-holder
+  .project-item-select-holder
     = project_select_tag :project_path, class: "project-item-select", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at' }
     %a.btn.btn-new.new-project-item-select-button
-      = icon('plus')
       = local_assigns[:label]
       %b.caret
 
diff --git a/app/views/shared/_sort_dropdown.html.haml b/app/views/shared/_sort_dropdown.html.haml
index a2f70c4b4745b0abb43cbf49a5dac698f891cde0..30f00aca960501a4120273404864115a99c8fa28 100644
--- a/app/views/shared/_sort_dropdown.html.haml
+++ b/app/views/shared/_sort_dropdown.html.haml
@@ -6,8 +6,10 @@
     - else
       = sort_title_recently_created
     %b.caret
-  %ul.dropdown-menu.dropdown-menu-align-right
+  %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-sort
     %li
+      = link_to page_filter_path(sort: sort_value_priority) do
+        = sort_title_priority
       = link_to page_filter_path(sort: sort_value_recently_created) do
         = sort_title_recently_created
       = link_to page_filter_path(sort: sort_value_oldest_created) do
diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml
index 40c6eb9be45931113bd94a0975dcd3afbfca877b..1ad953510056feb56e98f72dd76124d5d63aa94a 100644
--- a/app/views/shared/groups/_group.html.haml
+++ b/app/views/shared/groups/_group.html.haml
@@ -6,10 +6,10 @@
   - if group_member
     .controls.hidden-xs
       - if can?(current_user, :admin_group, group)
-        = link_to edit_group_path(group), class: "btn-sm btn btn-grouped" do
-          %i.fa.fa-cogs
+        = link_to edit_group_path(group), class: "btn" do
+          = icon('cogs')
 
-      = link_to leave_group_group_members_path(group), data: { confirm: leave_group_message(group.name) }, method: :delete, class: "btn-sm btn btn-grouped", title: 'Leave this group' do
+      = link_to leave_group_group_members_path(group), data: { confirm: leave_confirmation_message(group) }, method: :delete, class: "btn", title: 'Leave this group' do
         = icon('sign-out')
 
   .stats
diff --git a/app/views/shared/groups/_list.html.haml b/app/views/shared/groups/_list.html.haml
index 1aa7ed1f2eb9209657eb1bca6198a5f02c1818bd..427595c47a584bfd392381f9c8812f2adb5d3550 100644
--- a/app/views/shared/groups/_list.html.haml
+++ b/app/views/shared/groups/_list.html.haml
@@ -3,4 +3,4 @@
     - groups.each_with_index do |group, i|
       = render "shared/groups/group", group: group
 - else
-  %h3 No groups found
+  .nothing-here-block No groups found
diff --git a/app/views/shared/icons/_activity.svg b/app/views/shared/icons/_activity.svg
new file mode 100644
index 0000000000000000000000000000000000000000..d465504b154dc6c2ffecb12246f3641ed104d376
--- /dev/null
+++ b/app/views/shared/icons/_activity.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
+    <title>path-1</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="_activity" fill="#7E7D7D">
+            <g id="Page-1">
+                <g id="path-1">
+                    <path d="M5,0 C4.448,0 4,0.448 4,1 L4,3 L1,3 C0.448,3 0,3.448 0,4 L0,9 C0,9.552 0.448,10 1,10 L5,10 L5,8 L11,8 L11,10 L15,10 C15.552,10 16,9.552 16,9 L16,4 C16,3.448 15.552,3 15,3 L12,3 L12,1 C12,0.448 11.552,0 11,0 L5,0 L5,0 L5,0 L5,0 Z M6,2.5 C6,2.224 6.224,2 6.5,2 L9.5,2 C9.776,2 10,2.224 10,2.5 C10,2.776 9.776,3 9.5,3 L6.5,3 C6.224,3 6,2.776 6,2.5 L6,2.5 L6,2.5 L6,2.5 Z M6,11 L10.001,11 L10.001,9 L6,9 L6,11 L6,11 L6,11 L6,11 Z M11,11 L11,12 L5,12 L5,11 L1,11 C0.448,11 0,11.448 0,12 L0,15 C0,15.552 0.448,16 1,16 L15,16 C15.552,16 16,15.552 16,15 L16,12 C16,11.448 15.552,11 15,11 L11,11 L11,11 L11,11 L11,11 Z"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/app/views/shared/icons/_commits.svg b/app/views/shared/icons/_commits.svg
new file mode 100644
index 0000000000000000000000000000000000000000..ba9bb89935e6acb606c56065c96020dd0aedeba1
--- /dev/null
+++ b/app/views/shared/icons/_commits.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
+    <title>Pasted Image 240</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M3,8 C3,5.951 4.236,4.194 6,3.422 L6,0 L1,0 C0.448,0 0,0.448 0,1 L0,15 C0,15.552 0.448,16 1,16 L6,16 L6,12.578 C4.236,11.806 3,10.049 3,8 M7,12.899 L7,16 L9,16 L9,12.899 C8.677,12.965 8.343,13 8,13 C7.657,13 7.323,12.965 7,12.899 M15,0 L10,0 L10,3.422 C11.764,4.194 13,5.951 13,8 C13,10.049 11.764,11.806 10,12.578 L10,16 L15,16 C15.552,16 16,15.552 16,15 L16,1 C16,0.448 15.552,0 15,0 M10,8 C10,9.105 9.105,10 8,10 C6.895,10 6,9.105 6,8 C6,6.895 6.895,6 8,6 C9.105,6 10,6.895 10,8 M4,8 C4,10.209 5.791,12 8,12 C10.209,12 12,10.209 12,8 C12,5.791 10.209,4 8,4 C5.791,4 4,5.791 4,8 M9,3.101 L9,0 L7,0 L7,3.101 C7.323,3.035 7.657,3 8,3 C8.343,3 8.677,3.035 9,3.101" id="Pasted-Image-240" fill="#7E7D7D"></path>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/app/views/shared/icons/_contributionanalytics.svg b/app/views/shared/icons/_contributionanalytics.svg
new file mode 100644
index 0000000000000000000000000000000000000000..adf09a1496446c29f507473257c70ffefdfca9e5
--- /dev/null
+++ b/app/views/shared/icons/_contributionanalytics.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch -->
+    <title>Group</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="Group">
+            <path d="M8,0 C3.581,0 0,3.581 0,8 C0,12.419 3.581,16 8,16 C12.419,16 16,12.419 16,8 C16,3.581 12.419,0 8,0 M8,2 C11.308,2 14,4.692 14,8 C14,11.308 11.308,14 8,14 C4.692,14 2,11.308 2,8 C2,4.692 4.692,2 8,2" id="Fill-1" fill="#7E7C7C"></path>
+            <polygon id="Stroke-6" fill="#7E7C7C" points="2.0197351 9.86809696 6.4567351 6.52409696 5.79233671 6.46815759 9.53233671 10.4271576 9.87070552 10.78534 10.2338016 10.4522494 15.0258016 6.05624938 14.3497984 5.31935062 9.55779844 9.71535062 10.2592633 9.74044241 6.51926329 5.78144241 6.21208651 5.45627854 5.8548649 5.72550304 1.4178649 9.06950304"></polygon>
+            <path d="M7.0313,6.3928 C7.0313,6.9448 6.5833,7.3928 6.0313,7.3928 C5.4793,7.3928 5.0313,6.9448 5.0313,6.3928 C5.0313,5.8408 5.4793,5.3928 6.0313,5.3928 C6.5833,5.3928 7.0313,5.8408 7.0313,6.3928" id="Fill-8" fill="#FEFEFE"></path>
+            <path d="M6.5313,6.3928 C6.5313,6.66865763 6.30715763,6.8928 6.0313,6.8928 C5.75544237,6.8928 5.5313,6.66865763 5.5313,6.3928 C5.5313,6.11694237 5.75544237,5.8928 6.0313,5.8928 C6.30715763,5.8928 6.5313,6.11694237 6.5313,6.3928 L6.5313,6.3928 Z M7.5313,6.3928 C7.5313,5.56465763 6.85944237,4.8928 6.0313,4.8928 C5.20315763,4.8928 4.5313,5.56465763 4.5313,6.3928 C4.5313,7.22094237 5.20315763,7.8928 6.0313,7.8928 C6.85944237,7.8928 7.5313,7.22094237 7.5313,6.3928 L7.5313,6.3928 Z" id="Stroke-10" fill="#7E7C7C"></path>
+            <path d="M10.8854,9.8715 C10.8854,10.4235 10.4374,10.8715 9.8854,10.8715 C9.3334,10.8715 8.8854,10.4235 8.8854,9.8715 C8.8854,9.3195 9.3334,8.8715 9.8854,8.8715 C10.4374,8.8715 10.8854,9.3195 10.8854,9.8715" id="Fill-12" fill="#FEFEFE"></path>
+            <path d="M10.3854,9.8715 C10.3854,10.1473576 10.1612576,10.3715 9.8854,10.3715 C9.60954237,10.3715 9.3854,10.1473576 9.3854,9.8715 C9.3854,9.59564237 9.60954237,9.3715 9.8854,9.3715 C10.1612576,9.3715 10.3854,9.59564237 10.3854,9.8715 L10.3854,9.8715 Z M11.3854,9.8715 C11.3854,9.04335763 10.7135424,8.3715 9.8854,8.3715 C9.05725763,8.3715 8.3854,9.04335763 8.3854,9.8715 C8.3854,10.6996424 9.05725763,11.3715 9.8854,11.3715 C10.7135424,11.3715 11.3854,10.6996424 11.3854,9.8715 L11.3854,9.8715 Z" id="Stroke-14" fill="#7E7C7C"></path>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/app/views/shared/icons/_files.svg b/app/views/shared/icons/_files.svg
new file mode 100644
index 0000000000000000000000000000000000000000..fc378d81e40bcf3c8b5606337cc330069261f475
--- /dev/null
+++ b/app/views/shared/icons/_files.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
+    <title>Pasted Image 237</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="Pasted-Image-237">
+            <path d="M15.1111,16 C15.6021,16 16.0001,15.602 16.0001,15.111 L16.0001,4.444 C15.5341,3.983 12.0671,0.378 11.5551,0 L0.8891,0 C0.3981,0 0.0001,0.398 0.0001,0.889 L0.0001,15.111 C0.0001,15.602 0.3981,16 0.8891,16 L15.1111,16 M14.0001,14.111 L1.8891,14.111 L1.8891,2 L10.8131,2 C11.4451,2.42 13.5811,4.555 14.0001,5.187 L14.0001,14.111" id="Fill-1" fill="#7E7D7D"></path>
+            <path d="M0.889,0 C0.398,0 0,0.398 0,0.889 L0,15.111 C0,15.602 0.398,16 0.889,16 L15.111,16 C15.602,16 16,15.602 16,15.111 L16,4.445 C15.534,3.983 12.068,0.377 11.555,0 L0.889,0 L0.889,0 Z M1.889,2 L10.813,2 C11.446,2.42 13.581,4.554 14,5.187 L14,14.111 L1.889,14.111 L1.889,2 L1.889,2 Z" id="Clip-4"></path>
+            <polygon id="Fill-6" fill="#7E7D7D" points="9 7 11 7 11 2 9 2"></polygon>
+            <polygon id="Clip-9" points="9 7 11 7 11 2.001 9 2.001"></polygon>
+            <polygon id="Fill-11" fill="#7E7D7D" points="10 7 15.444 7 15.444 5 10 5"></polygon>
+            <polygon id="Clip-14" points="10 7 15.444 7 15.444 5 10 5"></polygon>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/app/views/shared/icons/_group.svg b/app/views/shared/icons/_group.svg
new file mode 100644
index 0000000000000000000000000000000000000000..75cae0d16c86a9de6d82e9270ba73545e6651a85
--- /dev/null
+++ b/app/views/shared/icons/_group.svg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch -->
+    <title>Group</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="Group" fill="#303030">
+            <path d="M15.6667,10.0105 L10.3337,10.0105 C10.1497,10.0105 9.9997,10.1775 9.9997,10.3845 L9.9997,15.6145 C9.9997,15.8215 10.1497,15.9885 10.3337,15.9885 L15.6667,15.9885 C15.8507,15.9885 15.9997,15.8215 15.9997,15.6145 L15.9997,10.3845 C15.9997,10.1775 15.8507,10.0105 15.6667,10.0105 L15.6667,10.0105 L15.6667,10.0105 Z M11.9997,14.0105 L13.9997,14.0105 L13.9997,12.0105 L11.9997,12.0105 L11.9997,14.0105 L11.9997,14.0105 Z" id="Fill-11"></path>
+            <path d="M5.6667,10.0105 L0.3337,10.0105 C0.1497,10.0105 -0.0003,10.1775 -0.0003,10.3845 L-0.0003,15.6145 C-0.0003,15.8215 0.1497,15.9885 0.3337,15.9885 L5.6667,15.9885 C5.8507,15.9885 5.9997,15.8215 5.9997,15.6145 L5.9997,10.3845 C5.9997,10.1775 5.8507,10.0105 5.6667,10.0105 L5.6667,10.0105 L5.6667,10.0105 Z M1.9997,14.0105 L3.9997,14.0105 L3.9997,12.0105 L1.9997,12.0105 L1.9997,14.0105 L1.9997,14.0105 Z" id="Fill-8"></path>
+            <polygon id="Stroke-1" points="12.5 7.5834 3.5 7.5834 3.5 9.5834 12.5 9.5834"></polygon>
+            <polygon id="Stroke-3" points="9 9.0834 9 5.0834 7 5.0834 7 9.0834"></polygon>
+            <polygon id="Stroke-4" points="4 11.0834 4 7.5834 2 7.5834 2 11.0834"></polygon>
+            <polygon id="Stroke-6" points="14 11.0834 14 7.5834 12 7.5834 12 11.0834"></polygon>
+            <path d="M11.6667,6.21724894e-15 L4.3337,6.21724894e-15 C4.1497,6.21724894e-15 3.9997,0.167 3.9997,0.374 L3.9997,6.604 C3.9997,6.811 4.1497,6.978 4.3337,6.978 L11.6667,6.978 C11.8507,6.978 11.9997,6.811 11.9997,6.604 L11.9997,0.374 C11.9997,0.167 11.8507,6.21724894e-15 11.6667,6.21724894e-15 L11.6667,6.21724894e-15 L11.6667,6.21724894e-15 Z M5.9997,5 L9.9997,5 L9.9997,2 L5.9997,2 L5.9997,5 L5.9997,5 Z" id="Fill-14"></path>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/app/views/shared/icons/_issues.svg b/app/views/shared/icons/_issues.svg
new file mode 100644
index 0000000000000000000000000000000000000000..2682c27ade972fea3bec517d476f614a8f42d15f
--- /dev/null
+++ b/app/views/shared/icons/_issues.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch -->
+    <title>Group</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="Group" fill="#7E7C7C">
+            <path d="M8,0 C3.581,0 0,3.581 0,8 C0,12.419 3.581,16 8,16 C12.419,16 16,12.419 16,8 C16,3.581 12.419,0 8,0 M8,2 C11.308,2 14,4.692 14,8 C14,11.308 11.308,14 8,14 C4.692,14 2,11.308 2,8 C2,4.692 4.692,2 8,2" id="Fill-1"></path>
+            <path d="M7.1597,4 L8.8887,4 L8.8887,8 L7.1107,8 L7.1597,4 Z M7.1597,9.6667 L8.8887,9.6667 L8.8887,11.4447 L7.1107,11.4447 L7.1597,9.6667 Z" id="Combined-Shape"></path>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/app/views/shared/icons/_members.svg b/app/views/shared/icons/_members.svg
new file mode 100644
index 0000000000000000000000000000000000000000..f8043b31fe8de91c37e8654b83dc3e9c4bf71ae2
--- /dev/null
+++ b/app/views/shared/icons/_members.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="22px" height="16px" viewBox="0 0 22 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch -->
+    <title>Group</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="Group" fill="#7E7C7C">
+            <path d="M6.4357,11.8588 C7.1487,11.2798 7.8797,10.7808 8.5357,10.3708 C8.5837,10.3008 8.6187,10.2338 8.6187,10.1768 L8.6187,8.8088 C8.9197,8.5218 9.0927,8.1248 9.0927,7.7028 L9.0927,5.3748 C9.0927,3.9478 7.9187,2.7858 6.4757,2.7858 L5.9687,2.7858 C4.5247,2.7858 3.3507,3.9478 3.3507,5.3748 L3.3507,7.7028 C3.3507,8.1248 3.5247,8.5218 3.8247,8.8088 L3.8247,10.5838 C3.2537,10.8738 1.8797,11.6198 0.5967,12.6618 C0.2177,12.9698 -0.0003,13.4258 -0.0003,13.9138 L-0.0003,15.5088 C-0.0003,15.5438 0.0857,15.7668 0.3467,15.7778 C1.3257,15.8198 3.8417,15.8328 5.9617,15.9038 C5.8337,15.8148 5.7447,15.6748 5.7447,15.5088 L5.7447,13.5498 C5.7447,12.9848 5.9967,12.2158 6.4357,11.8588" id="Fill-1"></path>
+            <path d="M21.3092,12.1 C19.6932,10.787 17.9592,9.86 17.3042,9.53 L17.3042,7.235 C17.6722,6.9 17.8862,6.428 17.8862,5.925 L17.8862,3.066 C17.8862,1.376 16.4952,0 14.7852,0 L14.1632,0 C12.4532,0 11.0622,1.376 11.0622,3.066 L11.0622,5.925 C11.0622,6.428 11.2752,6.9 11.6442,7.235 L11.6442,9.53 C10.9892,9.86 9.2542,10.787 7.6392,12.1 C7.2002,12.457 6.9482,12.985 6.9482,13.55 L6.9482,15.509 C6.9482,15.78 7.1702,16 7.4442,16 L14.1172,16 L14.1172,11.704 C12.6812,11.595 11.5652,10.853 11.5652,9.945 C11.5652,9.804 11.5982,9.669 11.6482,9.538 C11.9502,10.326 13.0982,10.913 14.4762,10.913 C15.8532,10.913 17.0012,10.326 17.3032,9.538 C17.3532,9.669 17.3862,9.804 17.3862,9.945 C17.3862,10.793 16.4152,11.5 15.1172,11.679 L15.1172,16 L21.5032,16 C21.7772,16 22.0002,15.78 22.0002,15.509 L22.0002,13.55 C22.0002,12.985 21.7482,12.457 21.3092,12.1" id="Fill-4"></path>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/app/views/shared/icons/_milestones.svg b/app/views/shared/icons/_milestones.svg
new file mode 100644
index 0000000000000000000000000000000000000000..3d62ecc06315940f9a1b29f9b1d84438c784bd88
--- /dev/null
+++ b/app/views/shared/icons/_milestones.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="16px" height="17px" viewBox="0 0 16 17" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch -->
+    <title>Group</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="Group" fill="#7E7C7C">
+            <path d="M15.1111,1 L0.8891,1 C0.3981,1 0.0001,1.446 0.0001,1.996 L0.0001,15.945 C0.0001,16.495 0.3981,16.941 0.8891,16.941 L15.1111,16.941 C15.6021,16.941 16.0001,16.495 16.0001,15.945 L16.0001,1.996 C16.0001,1.446 15.6021,1 15.1111,1 L15.1111,1 L15.1111,1 Z M14.0001,6.0002 L14.0001,14.949 L2.0001,14.949 L2.0001,6.0002 L14.0001,6.0002 Z M14.0001,4.0002 L14.0001,2.993 L2.0001,2.993 L2.0001,4.0002 L14.0001,4.0002 Z" id="Combined-Shape"></path>
+            <polygon id="Fill-11" points="3 2.0002 5 2.0002 5 0.0002 3 0.0002"></polygon>
+            <polygon id="Fill-16" points="11 2.0002 13 2.0002 13 0.0002 11 0.0002"></polygon>
+            <path d="M5.37709616,11.5511984 L6.92309616,12.7821984 C7.35112915,13.123019 7.97359761,13.0565604 8.32002627,12.6330535 L10.7740263,9.63305349 C11.1237073,9.20557058 11.0606364,8.57555475 10.6331535,8.22587373 C10.2056706,7.87619272 9.57565475,7.93926361 9.22597373,8.36674651 L6.77197373,11.3667465 L8.16890384,11.2176016 L6.62290384,9.98660159 C6.19085236,9.6425813 5.56172188,9.71394467 5.21770159,10.1459962 C4.8736813,10.5780476 4.94504467,11.2071781 5.37709616,11.5511984 L5.37709616,11.5511984 Z" id="Stroke-21"></path>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/app/views/shared/icons/_mr.svg b/app/views/shared/icons/_mr.svg
new file mode 100644
index 0000000000000000000000000000000000000000..dd3dbcc447316df8120457193619d22adf439a02
--- /dev/null
+++ b/app/views/shared/icons/_mr.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch -->
+    <title>Group</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="Group" fill="#7E7C7C">
+            <path d="M15.1111,0 L0.8891,0 C0.3981,0 0.0001,0.446 0.0001,0.996 L0.0001,14.945 C0.0001,15.495 0.3981,15.941 0.8891,15.941 L15.1111,15.941 C15.6021,15.941 16.0001,15.495 16.0001,14.945 L16.0001,0.996 C16.0001,0.446 15.6021,0 15.1111,0 L15.1111,0 L15.1111,0 Z M2.0001,13.949 L14.0001,13.949 L14.0001,1.993 L2.0001,1.993 L2.0001,13.949 Z M2,5.0002 L14,5.0002 L14,3.0002 L2,3.0002 L2,5.0002 Z" id="Combined-Shape"></path>
+            <path d="M8.547,12.0002 L12,12.0002 L12,10.0002 L8.547,10.0002 L8.547,12.0002 Z M5.2029,12 L3.9999,10.867 L5.2029,9.501 L3.9999,8.181 L5.2029,7 L7.4529,9.499 L5.2029,12 Z" id="Combined-Shape"></path>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/app/views/shared/icons/_pipelines.svg b/app/views/shared/icons/_pipelines.svg
new file mode 100644
index 0000000000000000000000000000000000000000..794e8a27025b5149d1ccbe1cd66c1a95a0a36c9c
--- /dev/null
+++ b/app/views/shared/icons/_pipelines.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
+    <title>Pasted Image 246</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M12.5,14 C11.672,14 11,13.328 11,12.5 C11,11.672 11.672,11 12.5,11 C13.328,11 14,11.672 14,12.5 C14,13.328 13.328,14 12.5,14 M12.5,9 L3.5,9 C1.567,9 0,10.567 0,12.5 C0,14.433 1.567,16 3.5,16 L12.5,16 C14.433,16 16,14.433 16,12.5 C16,10.567 14.433,9 12.5,9 M3.5,2 C4.328,2 5,2.672 5,3.5 C5,4.328 4.328,5 3.5,5 C2.672,5 2,4.328 2,3.5 C2,2.672 2.672,2 3.5,2 M3.5,7 L12.5,7 C14.433,7 16,5.433 16,3.5 C16,1.567 14.433,0 12.5,0 L3.5,0 C1.567,0 0,1.567 0,3.5 C0,5.433 1.567,7 3.5,7" id="Pasted-Image-246" fill="#303030"></path>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/app/views/shared/icons/_project.svg b/app/views/shared/icons/_project.svg
new file mode 100644
index 0000000000000000000000000000000000000000..1e8b43f8c6b0d3754f4eb9b4e925533f11b50740
--- /dev/null
+++ b/app/views/shared/icons/_project.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
+    <title>Page 1</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M6,6 L12,6 L12,5 L6,5 L6,6 Z M6,8 L12,8 L12,7 L6,7 L6,8 Z M6,10 L12,10 L12,9 L6,9 L6,10 Z M6,12 L12,12 L12,11 L6,11 L6,12 Z M4,6 L5,6 L5,5 L4,5 L4,6 Z M4,8 L5,8 L5,7 L4,7 L4,8 Z M4,10 L5,10 L5,9 L4,9 L4,10 Z M4,12 L5,12 L5,11 L4,11 L4,12 Z M13,3 L10,3 L10,4 L6,4 L6,3 L3,3 L3,13 L13,13 L13,3 Z M2,14 L14,14 L14,2 L2,2 L2,14 Z M1,0 C0.448,0 0,0.448 0,1 L0,15 C0,15.552 0.448,16 1,16 L15,16 C15.552,16 16,15.552 16,15 L16,1 C16,0.448 15.552,0 15,0 L1,0 Z" fill="#7F7E7E"></path>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/app/views/shared/icons/_wiki.svg b/app/views/shared/icons/_wiki.svg
new file mode 100644
index 0000000000000000000000000000000000000000..182d91e23aad7ccd78b4253adf75ee70b01aef77
--- /dev/null
+++ b/app/views/shared/icons/_wiki.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
+    <title>Pasted Image 241</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M2.004,12.9999459 L3.939,12.9999459 L3.939,4.99994585 L2.004,4.99994585 L2.004,12.9999459 Z M7.017,9.99994585 L13.018,9.99994585 L13.018,8.99994585 L7.017,8.99994585 L7.017,9.99994585 Z M7.017,7.99994585 L13.018,7.99994585 L13.018,6.99994585 L7.017,6.99994585 L7.017,7.99994585 Z M7.017,5.99994585 L13.018,5.99994585 L13.018,4.99994585 L7.017,4.99994585 L7.017,5.99994585 Z M14.754,-5.41499267e-05 L4.938,-5.41499267e-05 C4.386,-5.41499267e-05 3.938,0.44794585 3.938,0.99994585 L3.938,2.99994585 L1,2.99994585 C0.448,2.99994585 0,3.44794585 0,3.99994585 L0,12.9999459 C0.037,13.4999459 -0.25,16.0509459 3.938,15.9999459 L12.408,15.9999459 C12.408,15.9999459 15.754,15.9169459 15.754,13.9999459 L15.754,0.99994585 C15.754,0.44794585 15.306,-5.41499267e-05 14.754,-5.41499267e-05 L14.754,-5.41499267e-05 Z" id="Pasted-Image-241" fill="#7E7D7D"></path>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index 032854e399be0ff81851a23b347eb4f955618fdb..0af3b65a07e969823b3b00da122a60c3cbc11b81 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -1,6 +1,8 @@
 .issues-filters
   .issues-details-filters.row-content-block.second-block
-    = form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name]), method: :get, class: 'filter-form' do
+    = form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name, :issue_search]), method: :get, class: 'filter-form js-filter-form' do
+      - if params[:issue_search].present?
+        = hidden_field_tag :issue_search, params[:issue_search]
       - if controller.controller_name == 'issues' && can?(current_user, :admin_issue, @project)
         .check-all-holder
           = check_box_tag "check_all_issues", nil, false,
@@ -10,7 +12,7 @@
           - if params[:author_id].present?
             = hidden_field_tag(:author_id, params[:author_id])
           = dropdown_tag(user_dropdown_label(params[:author_id], "Author"), options: { toggle_class: "js-user-search js-filter-submit js-author-search", title: "Filter by author", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit",
-            placeholder: "Search authors", data: { any_user: "Any Author", first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author_id], field_name: "author_id", default_label: "Author" } })
+            placeholder: "Search authors", data: { any_user: "Any Author", first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author], field_name: "author_id", default_label: "Author" } })
 
         .filter-item.inline
           - if params[:assignee_id].present?
@@ -41,7 +43,7 @@
 
     - if controller.controller_name == 'issues'
       .issues_bulk_update.hide
-        = form_tag bulk_update_namespace_project_issues_path(@project.namespace, @project), method: :post  do
+        = form_tag bulk_update_namespace_project_issues_path(@project.namespace, @project), method: :post, class: 'bulk-update'  do
           .filter-item.inline
             = dropdown_tag("Status", options: { toggle_class: "js-issue-status", title: "Change status", dropdown_class: "dropdown-menu-status dropdown-menu-selectable", data: { field_name: "update[state_event]" } } ) do
               %ul
@@ -54,6 +56,10 @@
               placeholder: "Search authors", data: { first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: @project.id, field_name: "update[assignee_id]" } })
           .filter-item.inline
             = dropdown_tag("Milestone", options: { title: "Assign milestone", toggle_class: 'js-milestone-select js-extra-options js-filter-submit js-filter-bulk-update', filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone", placeholder: "Search milestones", data: { show_no: true, field_name: "update[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), use_id: true } })
+
+          .filter-item.inline.labels-filter
+            = render "shared/issuable/label_dropdown", classes: ['js-filter-bulk-update', 'js-multiselect'], show_create: false, show_footer: false, extra_options: false, filter_submit: false, show_footer: false, data_options: { persist_when_hide: "true", field_name: "update[label_ids][]", show_no: false, show_any: false, use_id: true }
+
           = hidden_field_tag 'update[issues_ids]', []
           = hidden_field_tag :state_event, params[:state_event]
           .filter-item.inline
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index 362cebfe3b74c73dc7a73e2b190b393d51846272..fc9cb9ddb9fda1429375b2d2d2caf8e774ce772f 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -35,62 +35,69 @@
       .clearfix
       .error-alert
 
-- if issuable.is_a?(Issue) && !issuable.project.private?
+- if issuable.is_a?(Issue)
   .form-group
     .col-sm-offset-2.col-sm-10
       .checkbox
         = f.label :confidential do
           = f.check_box :confidential
-          This issue is confidential and should only be visible to team members
+          This issue is confidential and should only be visible to team members with at least Reporter access.
 
 - if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project)
+  - has_due_date = issuable.has_attribute?(:due_date)
   %hr
-  .form-group
-    .issue-assignee
-      = f.label :assignee_id, "Assignee", class: 'control-label'
-      .col-sm-10
-        .issuable-form-select-holder
-          = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
-              placeholder: 'Select assignee', class: 'custom-form-control', null_user: true,
-              selected: issuable.assignee_id, project: @target_project || @project,
-              first_user: true, current_user: true, include_blank: true)
-        &nbsp;
-        = link_to 'Assign to me', '#', class: 'btn assign-to-me-link'
-  .form-group
-    .issue-milestone
-      = f.label :milestone_id, "Milestone", class: 'control-label'
-      .col-sm-10
-        - if milestone_options(issuable).present?
+  .row
+    %div{ class: (has_due_date ? "col-lg-6" : "col-sm-12") }
+      .form-group.issue-assignee
+        = f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}"
+        .col-sm-10{ class: ("col-lg-8" if has_due_date) }
           .issuable-form-select-holder
-            = f.select(:milestone_id, milestone_options(issuable),
-              { include_blank: true }, { class: 'select2', data: { placeholder: 'Select milestone' } })
-        - else
-          .prepend-top-10
-          %span.light No open milestones available.
-        &nbsp;
-        - if can? current_user, :admin_milestone, issuable.project
-          = link_to 'Create new milestone', new_namespace_project_milestone_path(issuable.project.namespace, issuable.project), target: :blank
-  - if issuable.respond_to?(:weight)
-    .form-group
-      = f.label :label_ids, class: 'control-label' do
-        Weight
-      .col-sm-10
-        = f.select :weight, issues_weight_options(issuable.weight, edit: true), { include_blank: true },
-          { class: 'select2 js-select2', data: { placeholder: "Select weight" }}
-
-  .form-group
-    - has_labels = issuable.project.labels.any?
-    = f.label :label_ids, "Labels", class: 'control-label'
-    .col-sm-10{ class: ('issuable-form-padding-top' if !has_labels) }
-      - if has_labels
-        .issuable-form-select-holder
-          = f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
-            { selected: issuable.label_ids }, multiple: true, class: 'select2', data: { placeholder: "Select labels" }
-      - else
-        %span.light No labels yet.
-      &nbsp;
-      - if can? current_user, :admin_label, issuable.project
-        = link_to 'Create new label', new_namespace_project_label_path(issuable.project.namespace, issuable.project), target: :blank
+            = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
+                placeholder: 'Select assignee', class: 'custom-form-control', null_user: true,
+                selected: issuable.assignee_id, project: @target_project || @project,
+                first_user: true, current_user: true, include_blank: true)
+          %div
+            = link_to 'Assign to me', '#', class: 'assign-to-me-link prepend-top-5 inline'
+      .form-group.issue-milestone
+        = f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
+        .col-sm-10{ class: ("col-lg-8" if has_due_date) }
+          - if milestone_options(issuable).present?
+            .issuable-form-select-holder
+              = f.select(:milestone_id, milestone_options(issuable),
+                { include_blank: true }, { class: 'select2', data: { placeholder: 'Select milestone' } })
+          - else
+            .prepend-top-10
+            %span.light No open milestones available.
+          - if can? current_user, :admin_milestone, issuable.project
+            %div
+              = link_to 'Create new milestone', new_namespace_project_milestone_path(issuable.project.namespace, issuable.project), target: :blank, class: "prepend-top-5 inline"
+      - if issuable.respond_to?(:weight)
+        .form-group
+          = f.label :label_ids, class: "control-label #{"col-lg-4" if has_due_date}" do
+            Weight
+          .col-sm-10{ class: ("col-lg-8" if has_due_date) }
+            = f.select :weight, issues_weight_options(issuable.weight, edit: true), { include_blank: true },
+              { class: 'select2 js-select2', data: { placeholder: "Select weight" }}
+      .form-group
+        - has_labels = issuable.project.labels.any?
+        = f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
+        .col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
+          - if has_labels
+            .issuable-form-select-holder
+              = f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
+                { selected: issuable.label_ids }, multiple: true, class: 'select2', data: { placeholder: "Select labels" }
+          - else
+            %span.light No labels yet.
+          - if can? current_user, :admin_label, issuable.project
+            %div
+              = link_to 'Create new label', new_namespace_project_label_path(issuable.project.namespace, issuable.project), target: :blank, class: "prepend-top-5 inline"
+    - if has_due_date
+      .col-lg-6
+        .form-group
+          = f.label :due_date, "Due date", class: "control-label"
+          .col-sm-10
+            .issuable-form-select-holder
+              = f.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date"
 
 - if issuable.can_move?(current_user)
   %hr
@@ -98,9 +105,7 @@
     = label_tag :move_to_project_id, 'Move', class: 'control-label'
     .col-sm-10
       .issuable-form-select-holder
-        - projects = project_options(issuable, current_user, ability: :admin_issue)
-        = select_tag(:move_to_project_id, projects, include_blank: true,
-                     class: 'select2', data: { placeholder: 'Select project' })
+        = hidden_field_tag :move_to_project_id, nil, class: 'js-move-dropdown', data: { placeholder: 'Select project', projects_url: autocomplete_projects_path(project_id: @project.id) }
       &nbsp;
       %span{ data: { toggle: 'tooltip', placement: 'auto top' }, style: 'cursor: default',
       title: 'Moving an issue will copy the discussion to a different project and close it here. All participants will be notified of the new location.' }
diff --git a/app/views/shared/issuable/_label_dropdown.html.haml b/app/views/shared/issuable/_label_dropdown.html.haml
index 61fd1e9c335d7262658f73abdd857d968303cd57..d34d28f6736a11b63d98c13319e34f115d26f099 100644
--- a/app/views/shared/issuable/_label_dropdown.html.haml
+++ b/app/views/shared/issuable/_label_dropdown.html.haml
@@ -1,14 +1,25 @@
+- show_create = local_assigns.fetch(:show_create, true)
+- extra_options = local_assigns.fetch(:extra_options, true)
+- filter_submit = local_assigns.fetch(:filter_submit, true)
+- show_footer = local_assigns.fetch(:show_footer, true)
+- data_options = local_assigns.fetch(:data_options, {})
+- classes = local_assigns.fetch(:classes, [])
+- dropdown_data = {toggle: 'dropdown', field_name: 'label_name[]', show_no: "true", show_any: "true", selected: params[:label_name], project_id: @project.try(:id), labels: labels_filter_path, default_label: "Label"}
+- dropdown_data.merge!(data_options)
+- classes << 'js-extra-options' if extra_options
+- classes << 'js-filter-submit' if filter_submit
+
 - if params[:label_name].present?
   - if params[:label_name].respond_to?('any?')
     - params[:label_name].each do |label|
       = hidden_field_tag "label_name[]", label, id: nil
 .dropdown
-  %button.dropdown-menu-toggle.js-label-select.js-filter-submit.js-multiselect.js-extra-options{type: "button", data: {toggle: "dropdown", field_name: "label_name[]", show_no: "true", show_any: "true", selected: params[:label_name], project_id: @project.try(:id), labels: labels_filter_path, default_label: "Label"}}
+  %button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data}
     %span.dropdown-toggle-text
       = h(multi_label_name(params[:label_name], "Label"))
     = icon('chevron-down')
   .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
-    = render partial: "shared/issuable/label_page_default", locals: { title: "Filter by label" }
-    - if can? current_user, :admin_label, @project and @project
+    = render partial: "shared/issuable/label_page_default", locals: { title: "Filter by label", show_footer: show_footer, show_create: show_create }
+    - if show_create and @project and can?(current_user, :admin_label, @project)
       = render partial: "shared/issuable/label_page_create"
     = dropdown_loading
diff --git a/app/views/shared/issuable/_label_page_default.html.haml b/app/views/shared/issuable/_label_page_default.html.haml
index 7f4867417f78f76a519eff03dec1a5a2957f70e2..0acb825313991095783dd45daa02b96d75b25544 100644
--- a/app/views/shared/issuable/_label_page_default.html.haml
+++ b/app/views/shared/issuable/_label_page_default.html.haml
@@ -1,20 +1,22 @@
 - title = local_assigns.fetch(:title, 'Assign labels')
+- show_create = local_assigns.fetch(:show_create, true)
+- show_footer = local_assigns.fetch(:show_footer, true)
 - filter_placeholder = local_assigns.fetch(:filter_placeholder, 'Search labels')
 .dropdown-page-one
   = dropdown_title(title)
-  = dropdown_filter(filter_placeholder)
+  = dropdown_filter(filter_placeholder, search_id: "label-name")
   = dropdown_content
-  - if @project
+  - if @project && show_footer
     = dropdown_footer do
       %ul.dropdown-footer-list
-        - if can? current_user, :admin_label, @project
+        - if can?(current_user, :admin_label, @project)
           %li
             %a.dropdown-toggle-page{href: "#"}
               Create new
         %li
           = link_to namespace_project_labels_path(@project.namespace, @project), :"data-is-link" => true do
-            - if can? current_user, :admin_label, @project
+            - if show_create && @project && can?(current_user, :admin_label, @project)
               Manage labels
             - else
               View labels
-  = dropdown_loading
\ No newline at end of file
+  = dropdown_loading
diff --git a/app/views/shared/issuable/_search_form.html.haml b/app/views/shared/issuable/_search_form.html.haml
index afad48499b7d7dc85a81c0c2b033b6cca9f27470..186963b32b87c5621351f8d8563b5ba693049791 100644
--- a/app/views/shared/issuable/_search_form.html.haml
+++ b/app/views/shared/issuable/_search_form.html.haml
@@ -1,8 +1,2 @@
 = form_tag(path, method: :get, id: "issue_search_form", class: 'issue-search-form') do
   = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by name ...', class: 'form-control issue_search search-text-input input-short', spellcheck: false }
-  = hidden_field_tag :state, params['state']
-  = hidden_field_tag :scope, params['scope']
-  = hidden_field_tag :assignee_id, params['assignee_id']
-  = hidden_field_tag :author_id, params['author_id']
-  = hidden_field_tag :milestone_id, params['milestone_id']
-  = hidden_field_tag :label_id, params['label_id']
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 76c4d4e19e9d6c8fef35aa034879234991a2eda1..532d3f2ddf7fa514ec02f82718d277015a2f58ba 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -1,24 +1,21 @@
+- todo = has_todo(issuable)
 %aside.right-sidebar{ class: sidebar_gutter_collapsed_class }
   .issuable-sidebar
     - can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
     .block.issuable-sidebar-header
-      %span.issuable-count.hide-collapsed.pull-left
-        = issuable.iid
-        of
-        = issuables_count(issuable)
-      %a.gutter-toggle.pull-right.js-sidebar-toggle{href: '#'}
+      - if current_user
+        %span.issuable-header-text.hide-collapsed.pull-left
+          Todo
+      %a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", aria: { label: "Toggle sidebar" } }
         = sidebar_gutter_toggle_icon
-      .issuable-nav.hide-collapsed.pull-right.btn-group{role: 'group', "aria-label" => '...'}
-        - if prev_issuable = prev_issuable_for(issuable)
-          = link_to 'Prev', [@project.namespace.becomes(Namespace), @project, prev_issuable], class: 'btn btn-default prev-btn issuable-pager'
-        - else
-          %a.btn.btn-default.issuable-pager.disabled{href: '#'}
-            Prev
-        - if next_issuable = next_issuable_for(issuable)
-          = link_to 'Next', [@project.namespace.becomes(Namespace), @project, next_issuable], class: 'btn btn-default next-btn issuable-pager'
-        - else
-          %a.btn.btn-default.issuable-pager.disabled{href: '#'}
-            Next
+      - if current_user
+        %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", aria: { label: (todo.nil? ? "Add Todo" : "Mark Done") }, data: { todo_text: "Add Todo", mark_text: "Mark Done", id: (todo.id unless todo.nil?), issuable: issuable.id, issuable_type: issuable.class.name.underscore, url: namespace_project_todos_path(@project.namespace, @project) } }
+          %span.js-issuable-todo-text
+            - if todo.nil?
+              Add Todo
+            - else
+              Mark Done
+          = icon('spin spinner', class: 'hidden js-issuable-todo-loading')
 
     = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f|
       .block.assignee
@@ -32,20 +29,21 @@
           = icon('spinner spin', class: 'block-loading')
           - if can_edit_issuable
             = link_to 'Edit', '#', class: 'edit-link pull-right'
-        .value.bold.hide-collapsed
+        .value.hide-collapsed
           - if issuable.assignee
-            = link_to_member(@project, issuable.assignee, size: 32) do
+            = link_to_member(@project, issuable.assignee, size: 32, extra_class: 'bold') do
               - if issuable.instance_of?(MergeRequest) && !issuable.can_be_merged_by?(issuable.assignee)
                 %span.pull-right.cannot-be-merged{ data: { toggle: 'tooltip', placement: 'left' }, title: 'Not allowed to merge' }
                   = icon('exclamation-triangle')
               %span.username
                 = issuable.assignee.to_reference
           - else
-            %span.assign-yourself
+            %span.assign-yourself.no-value
               No assignee
               - if can_edit_issuable
+                \-
                 %a.js-assign-yourself{ href: '#' }
-                  \- assign yourself
+                  assign yourself
 
         .selectbox.hide-collapsed
           = f.hidden_field 'assignee_id', value: issuable.assignee_id, id: 'issue_assignee_id'
@@ -56,7 +54,8 @@
           = icon('clock-o')
           %span
             - if issuable.milestone
-              = issuable.milestone.title
+              %span.has-tooltip{title: milestone_remaining_days(issuable.milestone), data: {container: 'body', html: 1, placement: 'left'}}
+                = issuable.milestone.title
             - else
               None
         .title.hide-collapsed
@@ -64,12 +63,11 @@
           = icon('spinner spin', class: 'block-loading')
           - if can_edit_issuable
             = link_to 'Edit', '#', class: 'edit-link pull-right'
-        .value.bold.hide-collapsed
+        .value.hide-collapsed
           - if issuable.milestone
-            = link_to namespace_project_milestone_path(@project.namespace, @project, issuable.milestone) do
-              = issuable.milestone.title
+            = link_to issuable.milestone.title, namespace_project_milestone_path(@project.namespace, @project, issuable.milestone), class: "bold has-tooltip", title: milestone_remaining_days(issuable.milestone), data: { container: "body", html: 1 }
           - else
-            .light None
+            %span.no-value None
 
         .selectbox.hide-collapsed
           = f.hidden_field 'milestone_id', value: issuable.milestone_id, id: nil
@@ -86,11 +84,17 @@
             = icon('spinner spin', class: 'block-loading')
             - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
               = link_to 'Edit', '#', class: 'edit-link pull-right'
-          .value.bold.hide-collapsed
-            - if issuable.due_date
-              = issuable.due_date.to_s(:medium)
-            - else
-              .light None
+          .value.hide-collapsed
+            %span.value-content
+              - if issuable.due_date
+                %span.bold= issuable.due_date.to_s(:medium)
+              - else
+                %span.no-value No due date
+            - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
+              %span.no-value.js-remove-due-date-holder{ class: ("hidden" if issuable.due_date.nil?) }
+                \-
+                %a.js-remove-due-date{ href: "#", role: "button" }
+                  remove due date
           - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
             .selectbox.hide-collapsed
               = f.hidden_field :due_date, value: issuable.due_date
@@ -108,20 +112,20 @@
           .sidebar-collapsed-icon
             = icon('tags')
             %span
-              = issuable.labels.count
+              = issuable.labels_array.size
           .title.hide-collapsed
             Labels
             = icon('spinner spin', class: 'block-loading')
             - if can_edit_issuable
               = link_to 'Edit', '#', class: 'edit-link pull-right'
-          .value.bold.issuable-show-labels.hide-collapsed{ class: ("has-labels" if issuable.labels.any?) }
-            - if issuable.labels.any?
-              - issuable.labels.each do |label|
+          .value.bold.issuable-show-labels.hide-collapsed{ class: ("has-labels" if issuable.labels_array.any?) }
+            - if issuable.labels_array.any?
+              - issuable.labels_array.each do |label|
                 = link_to_label(label, type: issuable.to_ability_name)
             - else
-              .light None
+              %span.no-value None
           .selectbox.hide-collapsed
-            - issuable.labels.each do |label|
+            - issuable.labels_array.each do |label|
               = hidden_field_tag "#{issuable.to_ability_name}[label_names][]", label.id, id: nil
             .dropdown
               %button.dropdown-menu-toggle.js-label-select.js-multiselect{type: "button", data: {toggle: "dropdown", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", project_id: (@project.id if @project), issue_update: issuable_json_path(issuable), labels: (namespace_project_labels_path(@project.namespace, @project, :json) if @project)}}
@@ -158,7 +162,7 @@
               %ul
                 - Issue.weight_options.select{|weight| weight != "Everything" && weight != "Any Weight"}.each do |weight|
                   %li
-                    %a{href: "#", data: { id: weight }, class: ("is-active" if params[:weight] == weight.to_s)}
+                    %a{href: "#", data: { id: weight, none: ("true" if weight == Issue::WEIGHT_NONE) }, class: ("is-active" if params[:weight] == weight.to_s)}
                       = weight
 
       = render "shared/issuable/participants", participants: issuable.participants(current_user)
@@ -170,7 +174,7 @@
           .title.hide-collapsed
             Notifications
           - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed'
-          %button.btn.btn-block.btn-gray.js-subscribe-button.issuable-subscribe-button.hide-collapsed{ type: "button" }
+          %button.btn.btn-block.btn-default.js-subscribe-button.issuable-subscribe-button.hide-collapsed{ type: "button" }
             %span= subscribed ? 'Unsubscribe' : 'Subscribe'
           .subscription-status.hide-collapsed{data: {status: subscribtion_status}}
             .unsubscribed{class: ( 'hidden' if subscribed )}
diff --git a/app/views/shared/members/_access_request_buttons.html.haml b/app/views/shared/members/_access_request_buttons.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..ed0a6ebcf84f119390c709bbb4123125651da321
--- /dev/null
+++ b/app/views/shared/members/_access_request_buttons.html.haml
@@ -0,0 +1,12 @@
+- member = source.members.find_by(user_id: current_user.id)
+
+- if member
+  - if member.request?
+    = link_to 'Withdraw Access Request', polymorphic_path([:leave, source, :members]),
+              method: :delete,
+              data: { confirm: remove_member_message(member) },
+              class: 'btn access-request-button hidden-xs'
+- else
+  = link_to 'Request Access', polymorphic_path([:request_access, source, :members]),
+            method: :post,
+            class: 'btn access-request-button hidden-xs'
diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..49f5a43f1eb1270c659d555082c6d97eea64dc48
--- /dev/null
+++ b/app/views/shared/members/_member.html.haml
@@ -0,0 +1,78 @@
+- default_show_roles = can?(current_user, action_member_permission(:update, member), member) || can?(current_user, action_member_permission(:destroy, member), member)
+- show_roles = local_assigns.fetch(:show_roles, default_show_roles)
+- show_controls = local_assigns.fetch(:show_controls, true)
+- user = member.user
+
+%li.js-toggle-container{ class: dom_class(member), id: dom_id(member) }
+  %span{ class: ("list-item-name" if show_controls) }
+    - if user
+      = image_tag avatar_icon(user, 24), class: "avatar s24", alt: ''
+      %strong
+        = link_to user.name, user_path(user)
+      %span.cgray= user.username
+
+      - if user == current_user
+        %span.label.label-success It's you
+
+      - if user.blocked?
+        %label.label.label-danger
+          %strong Blocked
+
+      - if member.request?
+        %span.cgray
+          – Requested
+          = time_ago_with_tooltip(member.requested_at)
+    - else
+      = image_tag avatar_icon(member.invite_email, 24), class: "avatar s24", alt: ''
+      %strong= member.invite_email
+      %span.cgray
+        – Invited
+        - if member.created_by
+          by
+          = link_to member.created_by.name, user_path(member.created_by)
+        = time_ago_with_tooltip(member.created_at)
+
+      - if show_controls && can?(current_user, action_member_permission(:admin, member), member.source)
+        = link_to 'Resend invite', polymorphic_path([:resend_invite, member]),
+                  method: :post,
+                  class: 'btn-xs btn'
+
+  - if show_roles && can_see_member_roles?(source: member.source, user: current_user)
+    %span.pull-right
+      %strong= member.human_access
+      - if show_controls
+        - if can?(current_user, action_member_permission(:update, member), member)
+          = button_tag icon('pencil'),
+                       type: 'button',
+                       class: 'btn-xs btn btn-grouped inline js-toggle-button',
+                       title: 'Edit access level'
+
+          - if member.request?
+            &nbsp;
+            = link_to icon('check inverse'), polymorphic_path([:approve_access_request, member]),
+                      method: :post,
+                      class: 'btn-xs btn btn-success',
+                      title: 'Grant access'
+
+        - if can?(current_user, action_member_permission(:destroy, member), member)
+          &nbsp;
+          - if current_user == user
+            = link_to icon('sign-out', text: 'Leave'), polymorphic_path([:leave, member.source, :members]),
+                      method: :delete,
+                      data: { confirm: leave_confirmation_message(member.source) },
+                      class: 'btn-xs btn btn-remove'
+          - else
+            = link_to icon('trash'), member,
+                      remote: true,
+                      method: :delete,
+                      data: { confirm: remove_member_message(member) },
+                      class: 'btn-xs btn btn-remove',
+                      title: remove_member_title(member)
+
+    .edit-member.hide.js-toggle-content
+      %br
+      = form_for member, remote: true do |f|
+        .prepend-top-10
+          = f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control'
+        .prepend-top-10
+          = f.submit 'Save', class: 'btn btn-save btn-sm'
diff --git a/app/views/shared/members/_requests.html.haml b/app/views/shared/members/_requests.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..b5963876034533e5ca288be635653559805e830b
--- /dev/null
+++ b/app/views/shared/members/_requests.html.haml
@@ -0,0 +1,8 @@
+- if members.any?
+  .panel.panel-default
+    .panel-heading
+      %strong= membership_source.name
+      access requests
+      %small= "(#{members.size})"
+    %ul.content-list
+      = render partial: 'shared/members/member', collection: members, as: :member
diff --git a/app/views/shared/milestones/_merge_requests_tab.haml b/app/views/shared/milestones/_merge_requests_tab.haml
index c29d8ee6737c1529953a566f552c77e235737c60..9c193f901e2bc208f765160790400ddd235a19fc 100644
--- a/app/views/shared/milestones/_merge_requests_tab.haml
+++ b/app/views/shared/milestones/_merge_requests_tab.haml
@@ -3,10 +3,10 @@
 
 .row.prepend-top-default
   .col-md-3
-    = render 'shared/milestones/issuables', args.merge(title: 'Work in progress (open and unassigned)', issuables: merge_requests.opened.unassigned, id: 'unassigned')
+    = render 'shared/milestones/issuables', args.merge(title: 'Work in progress (open and unassigned)', issuables: merge_requests.opened.unassigned, id: 'unassigned', show_counter: true)
   .col-md-3
-    = render 'shared/milestones/issuables', args.merge(title: 'Waiting for merge (open and assigned)', issuables: merge_requests.opened.assigned, id: 'ongoing')
+    = render 'shared/milestones/issuables', args.merge(title: 'Waiting for merge (open and assigned)', issuables: merge_requests.opened.assigned, id: 'ongoing', show_counter: true)
   .col-md-3
-    = render 'shared/milestones/issuables', args.merge(title: 'Rejected (closed)', issuables: merge_requests.closed, id: 'closed')
+    = render 'shared/milestones/issuables', args.merge(title: 'Rejected (closed)', issuables: merge_requests.closed, id: 'closed', show_counter: true)
   .col-md-3
-    = render 'shared/milestones/issuables', args.merge(title: 'Merged', issuables: merge_requests.merged, id: 'merged', primary: true)
+    = render 'shared/milestones/issuables', args.merge(title: 'Merged', issuables: merge_requests.merged, id: 'merged', primary: true, show_counter: true)
diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml
index 6b25745c55420f400ff8605739b345d8e5547e00..acc3ccf4dcf1796b5dbb659b2115339dde7b62ab 100644
--- a/app/views/shared/milestones/_milestone.html.haml
+++ b/app/views/shared/milestones/_milestone.html.haml
@@ -35,11 +35,9 @@
       .col-sm-6= 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" do
-            = icon('pencil-square-o')
+          = link_to edit_namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), class: "btn btn-xs btn-grouped" do
             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
-            = icon('trash-o')
+          = 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 btn-grouped"
+          = 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 btn-grouped" do
             Delete
diff --git a/app/views/shared/milestones/_participants_tab.html.haml b/app/views/shared/milestones/_participants_tab.html.haml
index 67ae85ac276fd567bc83afbb4adcfff40e129353..549d2e2f61e0e89ef4eb4f21298d95a26c73c67a 100644
--- a/app/views/shared/milestones/_participants_tab.html.haml
+++ b/app/views/shared/milestones/_participants_tab.html.haml
@@ -3,6 +3,6 @@
     %li
       = link_to user, title: user.name, class: "darken" do
         = image_tag avatar_icon(user, 32), class: "avatar s32"
-        %strong= truncate(user.name, lenght: 40)
+        %strong= truncate(user.name, length: 40)
         %br
         %small.cgray= user.username
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index 9ef021747a57353329b60b19eaf44ab91ec2d119..b8b66d08db8585e5c2a850f7ca3106af8e596ead 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -12,9 +12,6 @@
 %li.project-row{ class: css_class }
   = cache(cache_key) do
     .controls
-      - if project.main_language
-        %span
-          = project.main_language
       - if project.commit.try(:status)
         %span
           = render_commit_status(project.commit)
diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml
index e65b181487238d529f5b8e5196d0cbfdd83c16a1..af753496260b1c0e210cee2a3dddb4703c23ec7b 100644
--- a/app/views/shared/snippets/_header.html.haml
+++ b/app/views/shared/snippets/_header.html.haml
@@ -1,25 +1,24 @@
-.detail-page-header
-  .snippet-box.has-tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: 'body' }}
+.detail-page-header.clearfix
+  .snippet-box.has-tooltip.inline.append-right-5{ title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: "body" } }
+    %span.sr-only
+      = visibility_level_label(@snippet.visibility_level)
     = visibility_level_icon(@snippet.visibility_level, fw: false)
-    = visibility_level_label(@snippet.visibility_level)
-  %span.identifier
-    Snippet ##{@snippet.id}
+  %strong.item-title
+    Snippet #{@snippet.to_reference}
   %span.creator
-    &middot; created by #{link_to_member(@project, @snippet.author, size: 24)}
-    &middot;
+    created by #{link_to_member(@project, @snippet.author, size: 24, author_class: "author item-title")}
     = time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago')
     - if @snippet.updated_at != @snippet.created_at
       %span
-        &middot;
         = icon('edit', title: 'edited')
         = time_ago_with_tooltip(@snippet.updated_at, placement: 'bottom', html_class: 'snippet_edited_ago')
 
-  .pull-right
+  .snippet-actions
     - if @snippet.project_id?
       = render "projects/snippets/actions"
     - else
       = render "snippets/actions"
 
-.detail-page-description.row-content-block.second-block
-  %h2.title
-    = markdown escape_once(@snippet.title), pipeline: :single_line
+.content-block.second-block
+  %h2.snippet-title.prepend-top-0.append-bottom-0
+    = markdown escape_once(@snippet.title), pipeline: :single_line, author: @snippet.author
diff --git a/app/views/shared/web_hooks/_form.html.haml b/app/views/shared/web_hooks/_form.html.haml
index 287d7f8e1ff04eed04a8ffae483fc3a908c0db81..eccd9694743bca909dc07a04ff6fc939656cbd2a 100644
--- a/app/views/shared/web_hooks/_form.html.haml
+++ b/app/views/shared/web_hooks/_form.html.haml
@@ -6,7 +6,7 @@
     %h4.prepend-top-0
       = page_title
     %p
-      #{link_to "Webhooks ", help_page_path("web_hooks", "web_hooks"), class: "vlink"} can be
+      #{link_to "Webhooks", help_page_path("web_hooks", "web_hooks")} can be
       used for binding events when something is happening within the #{context_title}.
   .col-lg-9.append-bottom-default
     = form_for hook, as: :hook, url: polymorphic_path(url_components + [:hooks]) do |f|
@@ -65,6 +65,13 @@
                 %strong Build events
               %p.light
                 This url will be triggered when the build status changes
+          %li
+            = f.check_box :wiki_page_events, class: 'pull-left'
+            .prepend-left-20
+              = f.label :wiki_page_events, class: 'list-label' do
+                %strong Wiki Page events
+              %p.light
+                This url will be triggered when a wiki page is created/updated
       .form-group
         = f.label :enable_ssl_verification, "SSL verification", class: 'label-light checkbox'
         .checkbox
@@ -78,21 +85,7 @@
     - if hooks.any?
       %ul.well-list
         - hooks.each do |hook|
-          %li
-            .row
-              .col-md-8.col-lg-7
-                %strong.light-header= hook.url
-                %div
-                  - %w(push_events tag_push_events issues_events note_events merge_requests_events build_events).each do |trigger|
-                    - if hook.send(trigger)
-                      %span.label.label-gray.deploy-project-label= trigger.titleize
-              .col-md-4.col-lg-5.text-right-lg.prepend-top-5
-                %span.append-right-10.inline
-                  SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
-                = link_to 'Test', polymorphic_path(url_components + [hook], action: :test), class: "btn btn-sm"
-                = link_to polymorphic_path(url_components + [hook]), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-transparent" do
-                  %span.sr-only Remove
-                  = icon('trash')
+          = render "project_hook", hook: hook
     - else
       %p.settings-message.text-center.append-bottom-0
         No webhooks found, add one in the form above.
diff --git a/app/views/sherlock/queries/_backtrace.html.haml b/app/views/sherlock/queries/_backtrace.html.haml
index 5c9294c0ab534e0f2879707e06fa119483aab0e8..30e956e5f40ffb18a754e4643214cb93b44d0e2c 100644
--- a/app/views/sherlock/queries/_backtrace.html.haml
+++ b/app/views/sherlock/queries/_backtrace.html.haml
@@ -6,7 +6,11 @@
     %ul.well-list
       - @query.application_backtrace.each do |location|
         %li
-          = location.path
+          %strong
+            - if defined?(BetterErrors)
+              = link_to(location.path, BetterErrors.editor[location.path, location.line])
+            - else
+              = location.path
           %small.light
             = t('sherlock.line')
             = location.line
diff --git a/app/views/sherlock/queries/_general.html.haml b/app/views/sherlock/queries/_general.html.haml
index 549b47430e697f761c340f5738da302bda36839e..7073c0f4d90aad9ef931fdc7ad1196badd16e31e 100644
--- a/app/views/sherlock/queries/_general.html.haml
+++ b/app/views/sherlock/queries/_general.html.haml
@@ -11,13 +11,17 @@
           = @query.duration.round(4)
           = t('sherlock.milliseconds')
       %li
+        - frame = @query.last_application_frame
         %span.light
           #{t('sherlock.origin')}:
         %strong
-          = @query.last_application_frame.path
+          - if defined?(BetterErrors)
+            = link_to(frame.path, BetterErrors.editor[frame.path, frame.line])
+          - else
+            = frame.path
         %small.light
           = t('sherlock.line')
-          = @query.last_application_frame.line
+          = frame.line
 
   .panel.panel-default
     .panel-heading
diff --git a/app/views/snippets/_actions.html.haml b/app/views/snippets/_actions.html.haml
index 1979ae6d5bc473ebf218d9633bee889a747408cc..a7769654b61c280b5e8213c841bd13865a75a34e 100644
--- a/app/views/snippets/_actions.html.haml
+++ b/app/views/snippets/_actions.html.haml
@@ -1,11 +1,27 @@
-= link_to new_snippet_path, class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do
-  = icon('plus')
-  New Snippet
-- if can?(current_user, :update_personal_snippet, @snippet)
-  = link_to edit_snippet_path(@snippet), class: "btn btn-grouped snippable-edit" do
-    = icon('pencil-square-o')
-    Edit
-- if can?(current_user, :admin_personal_snippet, @snippet)
-  = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' do
-    = icon('trash-o')
-    Delete
+.hidden-xs
+  = link_to new_snippet_path, class: "btn btn-grouped btn-create new-snippet-link", title: "New Snippet" do
+    = icon('plus')
+    New Snippet
+  - if can?(current_user, :update_personal_snippet, @snippet)
+    = link_to edit_snippet_path(@snippet), class: "btn btn-grouped snippable-edit" do
+      Edit
+  - if can?(current_user, :admin_personal_snippet, @snippet)
+    = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-warning", title: 'Delete Snippet' do
+      Delete
+.visible-xs-block.dropdown
+  %button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } }
+    Options
+    %span.caret
+  .dropdown-menu.dropdown-menu-full-width
+    %ul
+      %li
+        = link_to new_snippet_path, title: "New Snippet" do
+          New Snippet
+      - if can?(current_user, :update_personal_snippet, @snippet)
+        %li
+          = link_to edit_snippet_path(@snippet) do
+            Edit
+      - if can?(current_user, :admin_personal_snippet, @snippet)
+        %li
+          = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do
+            Delete
diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml
index a2b365687700d5531210fffca18c35d178737bc8..ed3992650d4a0c741f809ab4f80e49e5cde3c29c 100644
--- a/app/views/snippets/show.html.haml
+++ b/app/views/snippets/show.html.haml
@@ -3,11 +3,10 @@
 .snippet-holder
   = render 'shared/snippets/header'
 
-  %article.file-holder
-    .file-title
+  %article.file-holder.file-holder-no-border.snippet-file-content
+    .file-title.file-title-clear
       = blob_icon 0, @snippet.file_name
-      %strong
-        = @snippet.file_name
+      = @snippet.file_name
       .file-actions.hidden-xs
         = clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{@snippet.id}']")
         = link_to 'Raw', raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank"
diff --git a/app/views/u2f/_authenticate.html.haml b/app/views/u2f/_authenticate.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..75fb0e303ada8cb196d366576b5c574e78960ea4
--- /dev/null
+++ b/app/views/u2f/_authenticate.html.haml
@@ -0,0 +1,28 @@
+#js-authenticate-u2f
+
+%script#js-authenticate-u2f-not-supported{ type: "text/template" }
+  %p Your browser doesn't support U2F. Please use Google Chrome desktop (version 41 or newer).
+
+%script#js-authenticate-u2f-setup{ type: "text/template" }
+  %div
+    %p Insert your security key (if you haven't already), and press the button below.
+    %a.btn.btn-info#js-login-u2f-device{ href: 'javascript:void(0)' } Login Via U2F Device
+
+%script#js-authenticate-u2f-in-progress{ type: "text/template" }
+  %p Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.
+
+%script#js-authenticate-u2f-error{ type: "text/template" }
+  %div
+    %p <%= error_message %>
+    %a.btn.btn-warning#js-u2f-try-again Try again?
+
+%script#js-authenticate-u2f-authenticated{ type: "text/template" }
+  %div
+    %p We heard back from your U2F device. Click this button to authenticate with the GitLab server.
+    = form_tag(new_user_session_path, method: :post) do |f|
+      = hidden_field_tag 'user[device_response]', nil, class: 'form-control', required: true, id: "js-device-response"
+      = submit_tag "Authenticate via U2F Device", class: "btn btn-success"
+
+:javascript
+  var u2fAuthenticate = new U2FAuthenticate($("#js-authenticate-u2f"), gon.u2f);
+  u2fAuthenticate.start();
diff --git a/app/views/u2f/_register.html.haml b/app/views/u2f/_register.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..cbb8dfb78296b940b85c6d0f5de63e0b11c97284
--- /dev/null
+++ b/app/views/u2f/_register.html.haml
@@ -0,0 +1,38 @@
+#js-register-u2f
+
+%script#js-register-u2f-not-supported{ type: "text/template" }
+  %p Your browser doesn't support U2F. Please use Google Chrome desktop (version 41 or newer).
+
+%script#js-register-u2f-setup{ type: "text/template" }
+  - if current_user.two_factor_otp_enabled?
+    .row.append-bottom-10
+      .col-md-3
+        %button#js-setup-u2f-device.btn.btn-info Setup New U2F Device
+      .col-md-9
+        %p Your U2F device needs to be set up. Plug it in (if not already) and click the button on the left.
+  - else
+    .row.append-bottom-10
+      .col-md-3
+        %button#js-setup-u2f-device.btn.btn-info{ disabled: true } Setup New U2F Device
+      .col-md-9
+        %p.text-warning You need to register a two-factor authentication app before you can set up a U2F device.
+
+%script#js-register-u2f-in-progress{ type: "text/template" }
+  %p Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.
+
+%script#js-register-u2f-error{ type: "text/template" }
+  %div
+    %p
+      %span <%= error_message %>
+    %a.btn.btn-warning#js-u2f-try-again Try again?
+
+%script#js-register-u2f-registered{ type: "text/template" }
+  %div.row.append-bottom-10
+    %p Your device was successfully set up! Click this button to register with the GitLab server.
+    = form_tag(create_u2f_profile_two_factor_auth_path, method: :post) do
+      = hidden_field_tag :device_response, nil, class: 'form-control', required: true, id: "js-device-response"
+      = submit_tag "Register U2F Device", class: "btn btn-success"
+
+:javascript
+  var u2fRegister = new U2FRegister($("#js-register-u2f"), gon.u2f);
+  u2fRegister.start();
diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml
index 1de71f37d1a9dd5bb4debcd3d500587b81d98d47..77f2ddefb1e28bff028e29c1cf807913363b222e 100644
--- a/app/views/users/calendar.html.haml
+++ b/app/views/users/calendar.html.haml
@@ -1,10 +1,9 @@
-#cal-heatmap.calendar
-  :javascript
-    new Calendar(
-      #{@timestamps.to_json},
-      #{@starting_year},
-      #{@starting_month},
-      '#{user_calendar_activities_path}'
-    );
-
-.calendar-hint Summary of issues, merge requests, and push events
+.clearfix.calendar
+  .js-contrib-calendar
+  .calendar-hint
+    Summary of issues, merge requests, and push events
+:javascript
+  new Calendar(
+    #{@timestamps.to_json},
+    '#{user_calendar_activities_path}'
+  );
diff --git a/app/views/users/calendar_activities.html.haml b/app/views/users/calendar_activities.html.haml
index 027a93a75fcd9ba317e1dc0b44c9e1b3035b1321..630d97e339db65a5fe81df7de03064c62b0dc6fa 100644
--- a/app/views/users/calendar_activities.html.haml
+++ b/app/views/users/calendar_activities.html.haml
@@ -1,23 +1,27 @@
 %h4.prepend-top-20
-  %span.light Contributions for
+  Contributions for
   %strong #{@calendar_date.to_s(:short)}
 
-%ul.bordered-list
-  - @events.sort_by(&:created_at).each do |event|
-    %li
-      %span.light
-        %i.fa.fa-clock-o
-        = event.created_at.to_s(:time)
-      - if event.push?
-        #{event.action_name} #{event.ref_type} #{event.ref_name}
-      - else
-        = event_action_name(event)
-        - if event.target
-          %strong= link_to "##{event.target_iid}", [event.project.namespace.becomes(Namespace), event.project, event.target]
-
-      at
-      %strong
-        - if event.project
-          = link_to_project event.project
+- if @events.any?
+  %ul.bordered-list
+    - @events.sort_by(&:created_at).each do |event|
+      %li
+        %span.light
+          %i.fa.fa-clock-o
+          = event.created_at.to_s(:time)
+        - if event.push?
+          #{event.action_name} #{event.ref_type} #{event.ref_name}
         - else
-          = event.project_name
+          = event_action_name(event)
+          - if event.target
+            %strong= link_to "##{event.target_iid}", [event.project.namespace.becomes(Namespace), event.project, event.target]
+
+        at
+        %strong
+          - if event.project
+            = link_to_project event.project
+          - else
+            = event.project_name
+- else
+  %p
+    No contributions found for #{@calendar_date.to_s(:short)}
diff --git a/app/views/users/show.atom.builder b/app/views/users/show.atom.builder
index e9e466c6350361697d0ddc605774108af483803d..6c85e5f9fbd531b7a7b38fcfe991714f1ffc2999 100644
--- a/app/views/users/show.atom.builder
+++ b/app/views/users/show.atom.builder
@@ -6,7 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
   xml.id      user_url(@user)
   xml.updated @events[0].updated_at.xmlschema if @events[0]
 
-  @events.each do |event|
-    event_to_atom(xml, event)
-  end
+  xml << render(@events) if @events.any?
 end
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 9017fd54fcccdc416ad31c3f227d4f3cac33001e..92305594a8167c1381dadc1ef100d2e7537d7817 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,5 +1,6 @@
 - page_title       @user.name
 - page_description @user.bio
+- page_specific_javascripts asset_path("users/application.js")
 - header_title     @user.name, user_path(@user)
 - @no_container = true
 
@@ -78,10 +79,10 @@
       %li.js-contributed-tab
         = link_to user_contributed_projects_path, data: {target: 'div#contributed', action: 'contributed', toggle: 'tab'} do
           Contributed projects
-      %li.projects-tab
+      %li.js-projects-tab
         = link_to user_projects_path, data: {target: 'div#projects', action: 'projects', toggle: 'tab'} do
           Personal projects
-      %li.snippets-tab
+      %li.js-snippets-tab
         = link_to user_snippets_path, data: {target: 'div#snippets', action: 'snippets', toggle: 'tab'} do
           Snippets
 
@@ -89,10 +90,9 @@
     .tab-content
       #activity.tab-pane
         .row-content-block.calender-block.white.second-block.hidden-xs
-          %div{ class: container_class }
-            .user-calendar{data: {href: user_calendar_path}}
-              %h4.center.light
-                %i.fa.fa-spinner.fa-spin
+          .user-calendar{data: {href: user_calendar_path}}
+            %h4.center.light
+              %i.fa.fa-spinner.fa-spin
           .user-calendar-activities
 
         .content_list{ data: {href: user_path} }
diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml
deleted file mode 100644
index 4beb87464444868a114e513741444a9fa8049aa0..0000000000000000000000000000000000000000
--- a/app/views/votes/_votes_block.html.haml
+++ /dev/null
@@ -1,30 +0,0 @@
-.awards.votes-block
-  - awards_sort(votable.notes.awards.grouped_awards).each do |emoji, notes|
-    %button.btn.award-control.js-emoji-btn.has-tooltip{class: (note_active_class(notes, current_user)), data: {placement: "top", original_title: emoji_author_list(notes, current_user)}}
-      = emoji_icon(emoji, sprite: false)
-      %span.award-control-text.js-counter
-        = notes.count
-
-  - if current_user
-    %div.award-menu-holder.js-award-holder
-      %a.btn.award-control.js-add-award{"href" => "#"}
-        = icon('smile-o', {class: "award-control-icon"})
-        = icon('spinner spin', {class: "award-control-icon award-control-icon-loading"})
-        %span.award-control-text
-          Add
-
-- if current_user
-  :javascript
-    var getEmojisUrl = "#{emojis_path}";
-    var postEmojiUrl = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}";
-    var noteableType = "#{votable.class.name.underscore}";
-    var noteableId = "#{votable.id}";
-    var unicodes = #{AwardEmoji.unicode.to_json};
-
-    window.awardsHandler = new AwardsHandler(
-      getEmojisUrl,
-      postEmojiUrl,
-      noteableType,
-      noteableId,
-      unicodes
-    );
diff --git a/app/workers/elastic_indexer_worker.rb b/app/workers/elastic_indexer_worker.rb
index 163f592e3b7d8b593e05273555373cc9751bba3b..6e2fcc8e9c9213e2f22ef439f5c9defb48cd76cc 100644
--- a/app/workers/elastic_indexer_worker.rb
+++ b/app/workers/elastic_indexer_worker.rb
@@ -1,10 +1,10 @@
 class ElasticIndexerWorker
   include Sidekiq::Worker
+  include Elasticsearch::Model::Client::ClassMethods
 
   sidekiq_options queue: :elasticsearch
 
-  Client = Elasticsearch::Client.new(host: Gitlab.config.elasticsearch.host,
-                                     port: Gitlab.config.elasticsearch.port)
+  ISSUE_TRACKED_FIELDS = %w(assignee_id author_id confidential)
 
   def perform(operation, class_name, record_id, options = {})
     klass = class_name.constantize
@@ -12,10 +12,12 @@ class ElasticIndexerWorker
     case operation.to_s
     when /index|update/
       record = klass.find(record_id)
-      record.__elasticsearch__.client = Client
+      record.__elasticsearch__.client = client
       record.__elasticsearch__.__send__ "#{operation}_document"
+
+      update_issue_notes(record, options["changed_fields"]) if klass == Issue
     when /delete/
-      Client.delete index: klass.index_name, type: klass.document_type, id: record_id
+      client.delete index: klass.index_name, type: klass.document_type, id: record_id
 
       clear_project_indexes(record_id) if klass == Project
     end
@@ -23,9 +25,15 @@ class ElasticIndexerWorker
     true # Less work to do!
   end
 
+  def update_issue_notes(record, changed_fields)
+    if changed_fields && (changed_fields & ISSUE_TRACKED_FIELDS).any?
+      Note.import query: -> { where(noteable: record) }
+    end
+  end
+
   def clear_project_indexes(record_id)
     # Remove repository index
-    Client.delete_by_query({
+    client.delete_by_query({
       index: Repository.__elasticsearch__.index_name,
       body: {
         query: {
diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb
index fa959fc56e3534236b73811af74663dddb1ea605..971f969e25e594a7c67ab4ebdaaa8e8e81d0c925 100644
--- a/app/workers/emails_on_push_worker.rb
+++ b/app/workers/emails_on_push_worker.rb
@@ -1,6 +1,7 @@
 class EmailsOnPushWorker
   include Sidekiq::Worker
 
+  sidekiq_options queue: :mailers
   attr_reader :email, :skip_premailer
 
   def perform(project_id, recipients, push_data, options = {})
diff --git a/app/workers/expire_build_artifacts_worker.rb b/app/workers/expire_build_artifacts_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c64ea108d52ef58067f1eb81d84ca4687a6e4f2c
--- /dev/null
+++ b/app/workers/expire_build_artifacts_worker.rb
@@ -0,0 +1,13 @@
+class ExpireBuildArtifactsWorker
+  include Sidekiq::Worker
+
+  def perform
+    Rails.logger.info 'Cleaning old build artifacts'
+
+    builds = Ci::Build.with_expired_artifacts
+    builds.find_each(batch_size: 50).each do |build|
+      Rails.logger.debug "Removing artifacts build #{build.id}..."
+      build.erase_artifacts!
+    end
+  end
+end
diff --git a/app/workers/geo_key_refresh_worker.rb b/app/workers/geo_key_refresh_worker.rb
index 76d8b0220bf5740a8b928453ff18c7b1549d9ab1..91ca9b6a73d1246d7f943e27963142ff18bb11e0 100644
--- a/app/workers/geo_key_refresh_worker.rb
+++ b/app/workers/geo_key_refresh_worker.rb
@@ -18,7 +18,7 @@ class GeoKeyRefreshWorker
       key = Key.new(id: key_id, key: key)
       key.remove_from_shell
     else
-      fail "Invalid action: #{action}"
+      raise "Invalid action: #{action}"
     end
   end
 end
diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb
index 921b9d20db9659e80cfa5cea9cb12a8ce55b8ab1..239535cf65f018bb5c55dee80fa3bd0ce346276c 100644
--- a/app/workers/post_receive.rb
+++ b/app/workers/post_receive.rb
@@ -1,5 +1,6 @@
 class PostReceive
   include Sidekiq::Worker
+  extend Gitlab::CurrentSettings
 
   sidekiq_options queue: :post_receive
 
@@ -50,7 +51,7 @@ class PostReceive
   end
 
   def update_wiki_es_indexes(post_received)
-    return unless Gitlab.config.elasticsearch.enabled
+    return unless current_application_settings.elasticsearch_indexing?
 
     post_received.project.wiki.index_blobs
   end
diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb
index f9e32337983943a1abc2ce2cf624aea1843d55fe..d947f1055160b7f4ae23d6d1c42ee8d74e17ea25 100644
--- a/app/workers/repository_fork_worker.rb
+++ b/app/workers/repository_fork_worker.rb
@@ -15,8 +15,7 @@ class RepositoryForkWorker
     result = gitlab_shell.fork_repository(source_path, target_path)
     unless result
       logger.error("Unable to fork project #{project_id} for repository #{source_path} -> #{target_path}")
-      project.update(import_error: "The project could not be forked.")
-      project.import_fail
+      project.mark_import_as_failed('The project could not be forked.')
       return
     end
 
@@ -24,8 +23,7 @@ class RepositoryForkWorker
 
     unless project.valid_repo?
       logger.error("Project #{project_id} had an invalid repository after fork")
-      project.update(import_error: "The forked repository is invalid.")
-      project.import_fail
+      project.mark_import_as_failed('The forked repository is invalid.')
       return
     end
 
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index 3ff796c1eb5d42429301e81d4427b4daa1140bd5..1ede1be10abf131a0d96020210601dbb9938bb38 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -13,8 +13,7 @@ class RepositoryImportWorker
     result = Projects::ImportService.new(project, current_user).execute
 
     if result[:status] == :error
-      project.update(import_error: Gitlab::UrlSanitizer.sanitize(result[:message]))
-      project.import_fail
+      project.mark_import_as_failed(result[:message])
       return
     end
 
diff --git a/app/workers/repository_update_mirror_worker.rb b/app/workers/repository_update_mirror_worker.rb
index 47a60f6a777c69c05f1047022f7b9339563a698a..8a0b23f602722e3c088e0b3e2768d8429f7dce53 100644
--- a/app/workers/repository_update_mirror_worker.rb
+++ b/app/workers/repository_update_mirror_worker.rb
@@ -2,11 +2,15 @@ class RepositoryUpdateMirrorWorker
   include Sidekiq::Worker
   include Gitlab::ShellAdapter
 
+  LEASE_TIMEOUT = 300
+  
   sidekiq_options queue: :gitlab_shell
 
   attr_accessor :project, :repository, :current_user
 
   def perform(project_id)
+    return unless try_obtain_lease(project_id)
+
     @project = Project.find(project_id)
     @current_user = @project.mirror_user || @project.creator
 
@@ -18,4 +22,12 @@ class RepositoryUpdateMirrorWorker
 
     project.import_finish
   end
+
+  private
+
+  def try_obtain_lease(project_id)
+    # Using 5 minutes timeout based on the 95th percent of timings (currently max of 25 seconds)
+    lease = ::Gitlab::ExclusiveLease.new("repository_update_mirror:#{project_id}", timeout: LEASE_TIMEOUT)
+    lease.try_obtain
+  end
 end
diff --git a/app/workers/stuck_ci_builds_worker.rb b/app/workers/stuck_ci_builds_worker.rb
index ce2b71933a35dfc888969b1cde2c26b045127eff..900b82ec46162c46bff8b16e2329abc86bdee3fe 100644
--- a/app/workers/stuck_ci_builds_worker.rb
+++ b/app/workers/stuck_ci_builds_worker.rb
@@ -7,7 +7,7 @@ class StuckCiBuildsWorker
     return if Gitlab::Geo.secondary?
     Rails.logger.info 'Cleaning stuck builds'
 
-    builds = Ci::Build.running_or_pending.where('updated_at < ?', BUILD_STUCK_TIMEOUT.ago)
+    builds = Ci::Build.joins(:project).running_or_pending.where('ci_builds.updated_at < ?', BUILD_STUCK_TIMEOUT.ago)
     builds.find_each(batch_size: 50).each do |build|
       Rails.logger.debug "Dropping stuck #{build.status} build #{build.id} for runner #{build.runner_id}"
       build.drop
diff --git a/app/workers/update_all_mirrors_worker.rb b/app/workers/update_all_mirrors_worker.rb
index 4d9c484bd8495176ab2b5e39ecc56c71bc038783..eb60ece65e80cb15c406c8a63fda999016203c51 100644
--- a/app/workers/update_all_mirrors_worker.rb
+++ b/app/workers/update_all_mirrors_worker.rb
@@ -1,10 +1,16 @@
 class UpdateAllMirrorsWorker
   include Sidekiq::Worker
 
+  LEASE_TIMEOUT = 3600
+
   def perform
+    return unless try_obtain_lease
+
     fail_stuck_mirrors!
 
-    Project.mirror.each(&:update_mirror)
+    Project.mirror.find_each(batch_size: 200) do |project|
+      project.update_mirror(delay: rand(30.minutes))
+    end
   end
 
   def fail_stuck_mirrors!
@@ -16,4 +22,12 @@ class UpdateAllMirrorsWorker
       project.mark_import_as_failed('The mirror update took too long to complete.')
     end
   end
+
+  private
+
+  def try_obtain_lease
+    # Using 30 minutes timeout based on the 95th percent of timings (currently max of 10 minutes)
+    lease = ::Gitlab::ExclusiveLease.new("update_all_mirrors", timeout: LEASE_TIMEOUT)
+    lease.try_obtain
+  end
 end
diff --git a/config/application.rb b/config/application.rb
index 1e30e18a5530c049b4c88e6f8c907898c4b65916..29ea4bfbce7089551bb30839c4ecf69dba8ee413 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -83,7 +83,10 @@ module Gitlab
     config.assets.precompile << "*.png"
     config.assets.precompile << "print.css"
     config.assets.precompile << "notify.css"
-    config.assets.precompile << "mailers/repository_push_email.css"
+    config.assets.precompile << "mailers/*.css"
+    config.assets.precompile << "graphs/application.js"
+    config.assets.precompile << "users/application.js"
+    config.assets.precompile << "network/application.js"
 
     # Version of your assets, change this if you want to expire all your assets
     config.assets.version = '1.0'
diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml
new file mode 100644
index 0000000000000000000000000000000000000000..436a2c5e17a76a4921faa1c2bbf65a2b0c76df04
--- /dev/null
+++ b/config/dependency_decisions.yml
@@ -0,0 +1,183 @@
+---
+# IGNORED GROUPS AND GEMS
+- - :ignore_group
+  - development
+  - :who: Connor Shea
+    :why: Development gems are not distributed with the final product and are therefore exempt.
+    :versions: []
+    :when: 2016-04-17 21:27:01.054140000 Z
+- - :ignore_group
+  - test
+  - :who: Connor Shea
+    :why: Test gems are not distributed with the final product and are therefore exempt.
+    :versions: []
+    :when: 2016-04-17 21:27:06.250326000 Z
+- - :ignore
+  - bundler
+  - :who: Connor Shea
+    :why: Bundler is MIT licensed but will sometimes fail in CI.
+    :versions: []
+    :when: 2016-05-02 06:42:08.045090000 Z
+
+# LICENSE WHITELIST
+- - :whitelist
+  - MIT
+  - :who: Connor Shea
+    :why: http://choosealicense.com/licenses/mit/
+    :versions: []
+    :when: 2016-04-17 21:12:24.558441000 Z
+- - :whitelist
+  - Apache 2.0
+  - :who: Connor Shea
+    :why: http://choosealicense.com/licenses/apache-2.0/
+    :versions: []
+    :when: 2016-05-02 05:27:43.762702000 Z
+- - :whitelist
+  - ruby
+  - :who: Connor Shea
+    :why: https://github.com/ruby/ruby/blob/ruby_2_1/COPYING
+    :versions: []
+    :when: 2016-05-02 05:31:54.498490000 Z
+- - :whitelist
+  - LGPL
+  - :who: Connor Shea
+    :why: http://www.gnu.org/licenses/license-list.html#LGPLv2.1
+    :versions: []
+    :when: 2016-05-02 05:32:48.645841000 Z
+- - :whitelist
+  - ISC
+  - :who: Connor Shea
+    :why: http://www.gnu.org/licenses/license-list.html#ISC
+    :versions: []
+    :when: 2016-05-02 05:42:01.894452000 Z
+- - :whitelist
+  - New BSD
+  - :who: Connor Shea
+    :why: https://opensource.org/licenses/BSD-3-Clause
+    :versions: []
+    :when: 2016-05-02 05:44:38.246021000 Z
+- - :whitelist
+  - LGPL-2.1+
+  - :who: Connor Shea
+    :why: Equivalent to LGPL.
+    :versions: []
+    :when: 2016-05-02 05:52:56.303239000 Z
+- - :whitelist
+  - BSD
+  - :who: Connor Shea
+    :why: https://opensource.org/licenses/BSD-2-Clause
+    :versions: []
+    :when: 2016-05-02 05:55:09.796363000 Z
+
+# LICENSE BLACKLIST
+- - :blacklist
+  - GPLv2
+  - :who: Connor Shea
+    :why: GPL-licensed libraries cannot be linked to from non-GPL projects.
+    :versions: []
+    :when: 2016-05-02 05:29:27.637336000 Z
+- - :blacklist
+  - GPLv3
+  - :who: Connor Shea
+    :why: GPL-licensed libraries cannot be linked to from non-GPL projects.
+    :versions: []
+    :when: 2016-05-02 05:29:43.904715000 Z
+
+# GEM LICENSES
+- - :license
+  - raphael-rails
+  - MIT
+  - :who: Connor Shea
+    :why: https://github.com/mockdeep/raphael-rails/blob/master/license.txt
+    :versions: []
+    :when: 2016-04-17 21:30:07.575392000 Z
+- - :license
+  - rouge
+  - MIT
+  - :who: Connor Shea
+    :why: https://github.com/jneen/rouge/blob/master/LICENSE
+    :versions: []
+    :when: 2016-04-17 21:31:29.490394000 Z
+- - :license
+  - pyu-ruby-sasl
+  - MIT
+  - :who: Connor Shea
+    :why: https://github.com/pyu10055/ruby-sasl/blob/master/MIT-LICENSE
+    :versions: []
+    :when: 2016-04-17 21:41:55.266420000 Z
+- - :license
+  - six
+  - MIT
+  - :who: Connor Shea
+    :why: https://github.com/randx/six/blob/master/LICENSE
+    :versions: []
+    :when: 2016-04-17 21:42:31.420186000 Z
+- - :license
+  - rdoc
+  - ruby
+  - :who: Connor Shea
+    :why: https://github.com/rdoc/rdoc/blob/master/LICENSE.rdoc
+    :versions: []
+    :when: 2016-04-17 21:43:30.480413000 Z
+- - :license
+  - expression_parser
+  - MIT
+  - :who: Connor Shea
+    :why: https://github.com/nricciar/expression_parser/blob/master/MIT-LICENSE
+    :versions: []
+    :when: 2016-04-17 21:45:41.829912000 Z
+- - :license
+  - creole
+  - ruby
+  - :who: Connor Shea
+    :why: https://github.com/minad/creole#license
+    :versions: []
+    :when: 2016-04-17 21:49:10.329759000 Z
+- - :license
+  - eventmachine
+  - ruby
+  - :who: Connor Shea
+    :why: https://github.com/eventmachine/eventmachine/blob/master/LICENSE
+    :versions: []
+    :when: 2016-04-17 21:49:10.329759001 Z
+- - :license
+  - unicorn
+  - ruby
+  - :who: Connor Shea
+    :why: http://unicorn.bogomips.org/LICENSE.html
+    :versions: []
+    :when: 2016-05-02 05:45:28.817510000 Z
+- - :license
+  - unicorn-worker-killer
+  - ruby
+  - :who: Connor Shea
+    :why: https://github.com/kzk/unicorn-worker-killer/blob/master/LICENSE
+    :versions: []
+    :when: 2016-05-02 05:45:38.323867000 Z
+- - :license
+  - json
+  - ruby
+  - :who: Connor Shea
+    :why: https://github.com/flori/json/tree/master#license
+    :versions: []
+    :when: 2016-05-02 05:50:07.826564000 Z
+- - :license
+  - unf
+  - BSD
+  - :who: Connor Shea
+    :why: https://github.com/knu/ruby-unf/blob/master/LICENSE
+    :versions: []
+    :when: 2016-05-02 05:51:46.886872000 Z
+- - :license
+  - rubypants
+  - BSD
+  - :who: Connor Shea
+    :why: https://github.com/jmcnevin/rubypants/blob/master/LICENSE.rdoc
+    :versions: []
+    :when: 2016-05-02 05:56:50.696858000 Z
+- - :whitelist
+  - LGPLv2+
+  - :who: Stan Hu
+    :why: Equivalent to LGPLv2
+    :versions: []
+    :when: 2016-06-07 17:14:10.907682000 Z
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 4f39016bfa4354cd5a26fe749ab4442aab3cf7d9..8cca0039b4af0da69f7491b6cd3d46038a7a7558 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -39,6 +39,7 @@ Rails.application.configure do
   config.action_mailer.delivery_method = :letter_opener_web
   # Don't make a mess when bootstrapping a development environment
   config.action_mailer.perform_deliveries = (ENV['BOOTSTRAP'] != '1')
+  config.action_mailer.preview_path = 'spec/mailers/previews'
 
   config.eager_load = false
 end
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 9a52362431ec90dff2f9135248d6eb8a1b5f8d21..02979a247c833f41b1652824f817e67f1d8d3721 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -150,14 +150,6 @@ production: &base
     # The location where LFS objects are stored (default: shared/lfs-objects).
     # storage_path: shared/lfs-objects
 
-  ## Elasticsearch (EE only)
-  # Enable it if you are going to use elasticsearch instead of
-  # regular database search
-  elasticsearch:
-    enabled: false
-  #  host: localhost
-  #  port: 9200
-
   ## GitLab Pages
   pages:
     enabled: false
@@ -187,6 +179,9 @@ production: &base
     # Flag stuck CI builds as failed
     stuck_ci_builds_worker:
       cron: "0 0 * * *"
+    # Remove expired build artifacts
+    expire_build_artifacts_worker:
+      cron: "50 * * * *"
     # Periodically run 'git fsck' on all repositories. If started more than
     # once per hour you will have concurrent 'git fsck' jobs.
     repository_check_worker:
@@ -228,11 +223,11 @@ production: &base
   registry:
     # enabled: true
     # host: registry.example.com
-    # port: 5000
-    # api_url: http://localhost:5000/
+    # port: 5005
+    # api_url: http://localhost:5000/ # internal address to the registry, will be used by GitLab to directly communicate with API
     # key: config/registry.key
-    # issuer: omnibus-certificate
     # path: shared/registry
+    # issuer: gitlab-issuer
 
   #
   # 2. GitLab CI settings
@@ -351,6 +346,12 @@ production: &base
         #
         admin_group: ''
 
+        # LDAP group of users who should be marked as external users in GitLab
+        #
+        #   Ex. ['Contractors', 'Interns']
+        #
+        external_groups: []
+
         # Name of attribute which holds a ssh public key of the user object.
         # If false or nil, SSH key syncronisation will be disabled.
         #
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 2a553c3f0c6fe274a7dfc80cdf08bda0d5546eaf..6c00b0a4a253849be6b0ef72bd5d7271e4aa5180 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -80,7 +80,7 @@ class Settings < Settingslogic
     # check that values in `current` (string or integer) is a contant in `modul`.
     def verify_constant_array(modul, current, default)
       values = default || []
-      if !current.nil?
+      unless current.nil?
         values = []
         current.each do |constant|
           values.push(verify_constant(modul, constant, nil))
@@ -157,6 +157,7 @@ if Settings.ldap['enabled'] || Rails.env.test?
     server['attributes'] = {} if server['attributes'].nil?
     server['provider_name'] ||= "ldap#{key}".downcase
     server['provider_class'] = OmniAuth::Utils.camelize(server['provider_name'])
+    server['external_groups'] = [] if server['external_groups'].nil?
     Settings.ldap['servers'][key] = server
   end
 end
@@ -338,6 +339,9 @@ Settings['cron_jobs'] ||= Settingslogic.new({})
 Settings.cron_jobs['stuck_ci_builds_worker'] ||= Settingslogic.new({})
 Settings.cron_jobs['stuck_ci_builds_worker']['cron'] ||= '0 0 * * *'
 Settings.cron_jobs['stuck_ci_builds_worker']['job_class'] = 'StuckCiBuildsWorker'
+Settings.cron_jobs['expire_build_artifacts_worker'] ||= Settingslogic.new({})
+Settings.cron_jobs['expire_build_artifacts_worker']['cron'] ||= '50 * * * *'
+Settings.cron_jobs['expire_build_artifacts_worker']['job_class'] = 'ExpireBuildArtifactsWorker'
 Settings.cron_jobs['repository_check_worker'] ||= Settingslogic.new({})
 Settings.cron_jobs['repository_check_worker']['cron'] ||= '20 * * * *'
 Settings.cron_jobs['repository_check_worker']['job_class'] = 'RepositoryCheck::BatchWorker'
@@ -419,8 +423,8 @@ Settings.satellites['path'] = File.expand_path(Settings.satellites['path'] || "t
 #
 Settings['kerberos'] ||= Settingslogic.new({})
 Settings.kerberos['enabled'] = false if Settings.kerberos['enabled'].nil?
-Settings.kerberos['keytab'] = nil if Settings.kerberos['keytab'].blank? #nil means use default keytab
-Settings.kerberos['service_principal_name'] = nil if Settings.kerberos['service_principal_name'].blank? #nil means any SPN in keytab
+Settings.kerberos['keytab'] = nil if Settings.kerberos['keytab'].blank? # nil means use default keytab
+Settings.kerberos['service_principal_name'] = nil if Settings.kerberos['service_principal_name'].blank? # nil means any SPN in keytab
 Settings.kerberos['use_dedicated_port'] = false if Settings.kerberos['use_dedicated_port'].nil?
 Settings.kerberos['https'] = Settings.gitlab.https if Settings.kerberos['https'].nil?
 Settings.kerberos['port'] ||= Settings.kerberos.https ? 8443 : 8088
diff --git a/config/initializers/chronic_duration.rb b/config/initializers/chronic_duration.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b65b06c813a4f8afeefd972f7f51fa15b9da4c89
--- /dev/null
+++ b/config/initializers/chronic_duration.rb
@@ -0,0 +1 @@
+ChronicDuration.raise_exceptions = true
diff --git a/config/initializers/devise_async.rb b/config/initializers/devise_async.rb
deleted file mode 100644
index 05a1852cdbd9d141a757755983adc59ad467f3ca..0000000000000000000000000000000000000000
--- a/config/initializers/devise_async.rb
+++ /dev/null
@@ -1 +0,0 @@
-Devise::Async.backend = :sidekiq
diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb
index 66ac88e9f4af7df56fa80799c49afe4e3debb9d6..618dba74151b52275cfdaba7698f82c3ba496710 100644
--- a/config/initializers/doorkeeper.rb
+++ b/config/initializers/doorkeeper.rb
@@ -12,7 +12,7 @@ Doorkeeper.configure do
   end
 
   resource_owner_from_credentials do |routes|
-    Gitlab::Auth.new.find(params[:username], params[:password])
+    Gitlab::Auth.find_with_user_password(params[:username], params[:password])
   end
 
   # If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below.
@@ -52,7 +52,7 @@ Doorkeeper.configure do
   # For more information go to
   # https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
   default_scopes  :api
-  #optional_scopes :write, :update
+  # optional_scopes :write, :update
 
   # Change the way client credentials are retrieved from the request object.
   # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
@@ -71,7 +71,7 @@ Doorkeeper.configure do
   # The value can be any string. Use nil to disable this feature. When disabled, clients must provide a valid URL
   # (Similar behaviour: https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi)
   #
-  native_redirect_uri nil#'urn:ietf:wg:oauth:2.0:oob'
+  native_redirect_uri nil # 'urn:ietf:wg:oauth:2.0:oob'
 
   # Specify what grant flows are enabled in array of Strings. The valid
   # strings and the flows they enable are:
diff --git a/config/initializers/elastic_client_setup.rb b/config/initializers/elastic_client_setup.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e1ed9a47f9af74e3cf40df4c9198d29df5ccbf86
--- /dev/null
+++ b/config/initializers/elastic_client_setup.rb
@@ -0,0 +1,33 @@
+# Be sure to restart your server when you modify this file.
+
+require 'gitlab/current_settings'
+
+
+module Elasticsearch
+  module Model
+    module Client
+      module ClassMethods
+        include Gitlab::CurrentSettings
+
+        def client(client = nil)
+          if @client.nil? || es_configuration_changed?
+            @es_host = current_application_settings.elasticsearch_host
+            @es_port = current_application_settings.elasticsearch_port
+
+            @client = Elasticsearch::Client.new(
+              host: current_application_settings.elasticsearch_host,
+              port: current_application_settings.elasticsearch_port
+            )
+          end
+
+          @client
+        end
+
+        def es_configuration_changed?
+          @es_host != current_application_settings.elasticsearch_host ||
+          @es_port != current_application_settings.elasticsearch_port
+        end
+      end
+    end
+  end
+end
diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb
index 9e8b0131f8ff7e3d3923697eca0b0ffdc77b19ce..3d1a41a46525d29327b2f7f0db903fb5a6794acf 100644
--- a/config/initializers/inflections.rb
+++ b/config/initializers/inflections.rb
@@ -8,3 +8,7 @@
 #   inflect.irregular 'person', 'people'
 #   inflect.uncountable %w( fish sheep )
 # end
+#
+ActiveSupport::Inflector.inflections do |inflect|
+  inflect.uncountable %w(award_emoji)
+end
diff --git a/config/initializers/metrics.rb b/config/initializers/metrics.rb
index 2338916e9da1bd6651645bcea24e461a2bbacd66..2aa11c8b0375ff5321b2647fe0fd54ee9ae7d0ed 100644
--- a/config/initializers/metrics.rb
+++ b/config/initializers/metrics.rb
@@ -12,6 +12,7 @@ if Gitlab::Metrics.enabled?
 
   Gitlab::Application.configure do |config|
     config.middleware.use(Gitlab::Metrics::RackMiddleware)
+    config.middleware.use(Gitlab::Middleware::RailsQueueDuration)
   end
 
   Sidekiq.configure_server do |config|
@@ -95,13 +96,18 @@ if Gitlab::Metrics.enabled?
       config.instrument_instance_methods(const)
     end
 
-    # Instruments all Banzai filters
-    Dir[Rails.root.join('lib', 'banzai', 'filter', '*.rb')].each do |file|
-      klass = File.basename(file, File.extname(file)).camelize
-      const = Banzai::Filter.const_get(klass)
+    # Instruments all Banzai filters and reference parsers
+    {
+      Filter: Rails.root.join('lib', 'banzai', 'filter', '*.rb'),
+      ReferenceParser: Rails.root.join('lib', 'banzai', 'reference_parser', '*.rb')
+    }.each do |const_name, path|
+      Dir[path].each do |file|
+        klass = File.basename(file, File.extname(file)).camelize
+        const = Banzai.const_get(const_name).const_get(klass)
 
-      config.instrument_methods(const)
-      config.instrument_instance_methods(const)
+        config.instrument_methods(const)
+        config.instrument_instance_methods(const)
+      end
     end
 
     config.instrument_methods(Banzai::Renderer)
@@ -120,9 +126,31 @@ if Gitlab::Metrics.enabled?
     config.instrument_instance_methods(Gitlab::GitAccessWiki)
 
     config.instrument_instance_methods(API::Helpers)
+
+    config.instrument_instance_methods(RepositoryCheck::SingleRepositoryWorker)
   end
 
   GC::Profiler.enable
 
   Gitlab::Metrics::Sampler.new.start
+
+  Gitlab::Metrics::Instrumentation.configure do |config|
+    config.instrument_instance_methods(Gitlab::InsecureKeyFingerprint)
+  end
+
+  module TrackNewRedisConnections
+    def connect(*args)
+      val = super
+
+      if current_transaction = Gitlab::Metrics::Transaction.current
+        current_transaction.increment(:new_redis_connections, 1)
+      end
+
+      val
+    end
+  end
+
+  class ::Redis::Client
+    prepend TrackNewRedisConnections
+  end
 end
diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb
index d961ab7b2e790ee235a93e554cd85dfa2e06c800..46e9dcea920d0a8a8c830057536f166b3ea1f866 100644
--- a/config/initializers/omniauth.rb
+++ b/config/initializers/omniauth.rb
@@ -16,7 +16,7 @@ end
 
 OmniAuth.config.full_host = Settings.gitlab['base_url']
 OmniAuth.config.allowed_request_methods = [:post]
-#In case of auto sign-in, the GET method is used (users don't get to click on a button)
+# In case of auto sign-in, the GET method is used (users don't get to click on a button)
 OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_sign_in_with_provider.present?
 OmniAuth.config.before_request_phase do |env|
   OmniAuth::RequestForgeryProtection.call(env)
diff --git a/config/initializers/premailer.rb b/config/initializers/premailer.rb
index b9176688bc49e1274b89114b50a86f0c7b02faad..cb00d3cfe95be3180ecf877358b29ff27f5d7cf8 100644
--- a/config/initializers/premailer.rb
+++ b/config/initializers/premailer.rb
@@ -3,6 +3,6 @@ Premailer::Rails.config.merge!(
   generate_text_part: false,
   preserve_styles: true,
   remove_comments: true,
-  remove_ids: true,
+  remove_ids: false,
   remove_scripts: false
 )
diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb
index 599dabb9e503433877f90747510511fd787c7cca..0d9d87bac00a3169cf681e66f42c0bcbfab07da2 100644
--- a/config/initializers/session_store.rb
+++ b/config/initializers/session_store.rb
@@ -23,6 +23,6 @@ else
     secure: Gitlab.config.gitlab.https,
     httponly: true,
     expires_in: Settings.gitlab['session_expire_delay'] * 60,
-    path: (Rails.application.config.relative_url_root.nil?) ? '/' : Gitlab::Application.config.relative_url_root
+    path: Rails.application.config.relative_url_root.nil? ? '/' : Gitlab::Application.config.relative_url_root
   )
 end
diff --git a/config/license_finder.yml b/config/license_finder.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e01ebec3298b99464771b266c37fcce4b9131e48
--- /dev/null
+++ b/config/license_finder.yml
@@ -0,0 +1,2 @@
+---
+decisions_file: './config/dependency_decisions.yml'
diff --git a/config/mail_room.yml b/config/mail_room.yml
index 761a32adb9ee4f14ace3031b0349182bcd1bf16d..7cab24b295e863ec0d19b95d25b198e74b6d80d8 100644
--- a/config/mail_room.yml
+++ b/config/mail_room.yml
@@ -2,7 +2,7 @@
 <%
 require "yaml"
 require "json"
-require_relative "lib/gitlab/redis"
+require_relative "lib/gitlab/redis" unless defined?(Gitlab::Redis)
 
 rails_env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
 
diff --git a/config/routes.rb b/config/routes.rb
index 3203a7a08521a0f428e62f201f7409f44ad546a0..b35903fb40e6aa8b27adec47a3a70cbe8605f55d 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -30,6 +30,11 @@ Rails.application.routes.draw do
     mount LetterOpenerWeb::Engine, at: '/rails/letter_opener'
   end
 
+  concern :access_requestable do
+    post :request_access, on: :collection
+    post :approve_access_request, on: :member
+  end
+
   namespace :ci do
     # CI API
     Ci::API::API.logger Rails.logger
@@ -53,15 +58,18 @@ Rails.application.routes.draw do
                 authorizations: 'oauth/authorizations'
   end
 
-  namespace :oauth, path: 'geo', controller: 'geo_auth', as: 'oauth_geo' do
-    get 'auth'
-    get 'callback'
-    get 'logout'
+  namespace :oauth do
+    scope path: 'geo', controller: :geo_auth, as: :geo do
+      get 'auth'
+      get 'callback'
+      get 'logout'
+    end
   end
 
   # Autocomplete
   get '/autocomplete/users' => 'autocomplete#users'
   get '/autocomplete/users/:id' => 'autocomplete#user'
+  get '/autocomplete/projects' => 'autocomplete#projects'
 
   # Emojis
   resources :emojis, only: :index
@@ -85,8 +93,8 @@ Rails.application.routes.draw do
   # Health check
   get 'health_check(/:checks)' => 'health_check#index', as: :health_check
 
-  # Enable Grack support
-  mount Grack::AuthSpawner, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post, :put]
+  # Enable Grack support (for LFS only)
+  mount Grack::AuthSpawner, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\/(info\/lfs|gitlab-lfs)/.match(request.path_info) }, via: [:get, :post, :put]
 
   # Help
   get 'help'                  => 'help#index'
@@ -361,8 +369,9 @@ Rails.application.routes.draw do
       resources :keys
       resources :emails, only: [:index, :create, :destroy]
       resource :avatar, only: [:destroy]
-      resource :two_factor_auth, only: [:new, :create, :destroy] do
+      resource :two_factor_auth, only: [:show, :create, :destroy] do
         member do
+          post :create_u2f
           post :codes
           patch :skip
         end
@@ -438,7 +447,7 @@ Rails.application.routes.draw do
       end
 
       resources :ldap_group_links, only: [:index, :create, :destroy]
-      resources :group_members, only: [:index, :create, :update, :destroy] do
+      resources :group_members, only: [:index, :create, :update, :destroy], concerns: :access_requestable do
         post :resend_invite, on: :member
         delete :leave, on: :collection
       end
@@ -460,7 +469,11 @@ Rails.application.routes.draw do
   post 'unsubscribes/:email', to: 'unsubscribes#create'
   resources :projects, constraints: { id: /[^\/]+/ }, only: [:index, :new, :create]
 
-  devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations , passwords: :passwords, sessions: :sessions, confirmations: :confirmations }
+  devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks,
+                                    registrations: :registrations,
+                                    passwords: :passwords,
+                                    sessions: :sessions,
+                                    confirmations: :confirmations }
 
   devise_scope :user do
     get '/users/auth/:provider/omniauth_error' => 'omniauth_callbacks#omniauth_error', as: :omniauth_error
@@ -475,6 +488,7 @@ Rails.application.routes.draw do
   resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: [] do
     resources(:projects, constraints: { id: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ }, except:
               [:new, :create, :index], path: "/") do
+
       member do
         put :transfer
         delete :remove_fork
@@ -488,6 +502,29 @@ Rails.application.routes.draw do
       end
 
       scope module: :projects do
+        # Git HTTP clients ('git clone' etc.)
+        scope constraints: { id: /.+\.git/, format: nil } do
+          get '/info/refs', to: 'git_http#info_refs'
+          post '/git-upload-pack', to: 'git_http#git_upload_pack'
+          post '/git-receive-pack', to: 'git_http#git_receive_pack'
+        end
+
+        # Allow /info/refs, /info/refs?service=git-upload-pack, and
+        # /info/refs?service=git-receive-pack, but nothing else.
+        #
+        git_http_handshake = lambda do |request|
+          request.query_string.blank? ||
+            request.query_string.match(/\Aservice=git-(upload|receive)-pack\z/)
+        end
+
+        ref_redirect = redirect do |params, request|
+          path = "#{params[:namespace_id]}/#{params[:project_id]}.git/info/refs"
+          path << "?#{request.query_string}" unless request.query_string.blank?
+          path
+        end
+
+        get '/info/refs', constraints: git_http_handshake, to: ref_redirect
+
         # Blob routes:
         get '/new/*id', to: 'blob#new', constraints: { id: /.+/ }, as: 'new_blob'
         post '/create/*id', to: 'blob#create', constraints: { id: /.+/ }, as: 'create_blob'
@@ -630,7 +667,6 @@ Rails.application.routes.draw do
           # Order matters to give priority to these matches
           get '/wikis/git_access', to: 'wikis#git_access'
           get '/wikis/pages', to: 'wikis#pages', as: 'wiki_pages'
-          post '/wikis/markdown_preview', to:'wikis#markdown_preview'
           post '/wikis', to: 'wikis#create'
 
           get '/wikis/*id/history', to: 'wikis#history', as: 'wiki_history', constraints: WIKI_SLUG_ID
@@ -639,6 +675,7 @@ Rails.application.routes.draw do
           get '/wikis/*id', to: 'wikis#show', as: 'wiki', constraints: WIKI_SLUG_ID
           delete '/wikis/*id', to: 'wikis#destroy', constraints: WIKI_SLUG_ID
           put '/wikis/*id', to: 'wikis#update', constraints: WIKI_SLUG_ID
+          post '/wikis/*id/markdown_preview', to:'wikis#markdown_preview', constraints: WIKI_SLUG_ID, as: 'wiki_markdown_preview'
         end
 
         resource :repository, only: [:show, :create] do
@@ -693,6 +730,7 @@ Rails.application.routes.draw do
             post :toggle_subscription
             post :approve
             post :rebase
+            post :toggle_award_emoji
             post :remove_wip
           end
 
@@ -708,6 +746,11 @@ Rails.application.routes.draw do
         resources :tags, only: [:index, :show, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } do
           resource :release, only: [:edit, :update]
         end
+        resources :path_locks, only: [:index, :destroy] do
+          collection do
+            post :toggle
+          end
+        end
 
         resources :protected_branches, only: [:index, :create, :update, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
         resources :variables, only: [:index, :show, :update, :create, :destroy]
@@ -726,6 +769,8 @@ Rails.application.routes.draw do
           end
         end
 
+        resources :environments, only: [:index, :show, :new, :create, :destroy]
+
         resources :builds, only: [:index, :show], constraints: { id: /\d+/ } do
           collection do
             post :cancel_all
@@ -744,6 +789,7 @@ Rails.application.routes.draw do
             get :download
             get :browse, path: 'browse(/*path)', format: false
             get :file, path: 'file/*path', format: false
+            post :keep
           end
         end
 
@@ -765,16 +811,19 @@ Rails.application.routes.draw do
         resources :labels, constraints: { id: /\d+/ } do
           collection do
             post :generate
+            post :set_priorities
           end
 
           member do
             post :toggle_subscription
+            delete :remove_priority
           end
         end
 
         resources :issues, constraints: { id: /\d+/ } do
           member do
             post :toggle_subscription
+            post :toggle_award_emoji
             get :referenced_merge_requests
             get :related_branches
             get :can_create_branch
@@ -784,7 +833,7 @@ Rails.application.routes.draw do
           end
         end
 
-        resources :project_members, except: [:new, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ } do
+        resources :project_members, except: [:new, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ }, concerns: :access_requestable do
           collection do
             delete :leave
 
@@ -803,14 +852,13 @@ Rails.application.routes.draw do
 
         resources :notes, only: [:index, :create, :destroy, :update], constraints: { id: /\d+/ } do
           member do
+            post :toggle_award_emoji
             delete :delete_attachment
           end
-
-          collection do
-            post :award_toggle
-          end
         end
 
+        resources :todos, only: [:create, :update], constraints: { id: /\d+/ }
+
         resources :uploads, only: [:create] do
           collection do
             get ":secret/:filename", action: :show, as: :show, constraints: { filename: /[^\/]+/ }
@@ -843,7 +891,7 @@ Rails.application.routes.draw do
   end
 
   # Get all keys of user
-  get ':username.keys' => 'profiles/keys#get_keys' , constraints: { username: /.*/ }
+  get ':username.keys' => 'profiles/keys#get_keys', constraints: { username: /.*/ }
 
   get ':id' => 'namespaces#show', constraints: { id: /(?:[^.]|\.(?!atom$))+/, format: /atom/ }
 end
diff --git a/db/fixtures/development/14_builds.rb b/db/fixtures/development/14_builds.rb
index 2cf340b2ca33a5a2a87dbadcfef17b9f38c2220f..9ec6ac668316aecc440a7d8ed17c873a4c443956 100644
--- a/db/fixtures/development/14_builds.rb
+++ b/db/fixtures/development/14_builds.rb
@@ -19,7 +19,7 @@ class Gitlab::Seeder::Builds
     commits = @project.repository.commits('master', path: nil, limit: 5)
     commits_sha = commits.map { |commit| commit.raw.id }
     commits_sha.map do |sha|
-      @project.ensure_ci_commit(sha, 'master')
+      @project.ensure_pipeline(sha, 'master')
     end
   rescue
     []
diff --git a/db/fixtures/development/15_award_emoji.rb b/db/fixtures/development/15_award_emoji.rb
new file mode 100644
index 0000000000000000000000000000000000000000..baac32f2d10038233a1dc65beea7f42e61b1dec3
--- /dev/null
+++ b/db/fixtures/development/15_award_emoji.rb
@@ -0,0 +1,33 @@
+Gitlab::Seeder.quiet do
+  emoji = Gitlab::AwardEmoji.emojis.keys
+
+  Issue.order(Gitlab::Database.random).limit(Issue.count / 2).each do |issue|
+    project = issue.project
+
+    project.team.users.sample(2).each do |user|
+      issue.create_award_emoji(emoji.sample, user)
+
+      issue.notes.sample(2).each do |note|
+        next if note.system?
+        note.create_award_emoji(emoji.sample, user)
+      end
+
+      print '.'
+    end
+  end
+
+  MergeRequest.order(Gitlab::Database.random).limit(MergeRequest.count / 2).each do |mr|
+    project = mr.project
+
+    project.team.users.sample(2).each do |user|
+      mr.create_award_emoji(emoji.sample, user)
+
+      mr.notes.sample(2).each do |note|
+        next if note.system?
+        note.create_award_emoji(emoji.sample, user)
+      end
+
+      print '.'
+    end
+  end
+end
diff --git a/db/fixtures/production/001_admin.rb b/db/fixtures/production/001_admin.rb
index 78746c832252ed91e94ec8d2b695f534cdd07880..b37dc794015882af2882c513096092feff472a8b 100644
--- a/db/fixtures/production/001_admin.rb
+++ b/db/fixtures/production/001_admin.rb
@@ -16,21 +16,21 @@ user = User.new(user_args)
 user.skip_confirmation!
 
 if user.save
-  puts "Administrator account created:".green
+  puts "Administrator account created:".color(:green)
   puts
-  puts "login:    root".green
+  puts "login:    root".color(:green)
 
   if user_args.key?(:password)
-    puts "password: #{user_args[:password]}".green
+    puts "password: #{user_args[:password]}".color(:green)
   else
-    puts "password: You'll be prompted to create one on your first visit.".green
+    puts "password: You'll be prompted to create one on your first visit.".color(:green)
   end
   puts
 else
-  puts "Could not create the default administrator account:".red
+  puts "Could not create the default administrator account:".color(:red)
   puts
   user.errors.full_messages.map do |message|
-    puts "--> #{message}".red
+    puts "--> #{message}".color(:red)
   end
   puts
 
diff --git a/db/migrate/20121220064453_init_schema.rb b/db/migrate/20121220064453_init_schema.rb
index d7644b6847af8aecfd5a307c5aa553bbbb0b419e..f93dc92b70ffa5ffecc26225d5e2ad090ac9b1dd 100644
--- a/db/migrate/20121220064453_init_schema.rb
+++ b/db/migrate/20121220064453_init_schema.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class InitSchema < ActiveRecord::Migration
   def up
 
diff --git a/db/migrate/20130102143055_rename_owner_to_creator_for_project.rb b/db/migrate/20130102143055_rename_owner_to_creator_for_project.rb
index d0fca269871dcfb475905d12fadaafca359e3614..84fd2060770c2baf4d01cc4d1b02a8f66b5800fc 100644
--- a/db/migrate/20130102143055_rename_owner_to_creator_for_project.rb
+++ b/db/migrate/20130102143055_rename_owner_to_creator_for_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RenameOwnerToCreatorForProject < ActiveRecord::Migration
   def change
     rename_column :projects, :owner_id, :creator_id
diff --git a/db/migrate/20130110172407_add_public_to_project.rb b/db/migrate/20130110172407_add_public_to_project.rb
index 45edba48152aabbd93949b25da9cd50d82f55569..4362aadcc1da2ef8caa6eec2ba8f322ddfe3dc2a 100644
--- a/db/migrate/20130110172407_add_public_to_project.rb
+++ b/db/migrate/20130110172407_add_public_to_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddPublicToProject < ActiveRecord::Migration
   def change
     add_column :projects, :public, :boolean, default: false, null: false
diff --git a/db/migrate/20130123114545_add_issues_tracker_to_project.rb b/db/migrate/20130123114545_add_issues_tracker_to_project.rb
index 288d0f07c9ae58c6d973cf128dd4241edc6071ff..ba8c50b53e2fa6689d50d4b25592bd1af1968401 100644
--- a/db/migrate/20130123114545_add_issues_tracker_to_project.rb
+++ b/db/migrate/20130123114545_add_issues_tracker_to_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddIssuesTrackerToProject < ActiveRecord::Migration
   def change
     add_column :projects, :issues_tracker, :string, default: :gitlab, null: false
diff --git a/db/migrate/20130125090214_add_user_permissions.rb b/db/migrate/20130125090214_add_user_permissions.rb
index 38b5f439a2d22687348aacc87ad919c51440ca6f..1350eadb60efedb87a919646bc1ba4ebf3c5b55a 100644
--- a/db/migrate/20130125090214_add_user_permissions.rb
+++ b/db/migrate/20130125090214_add_user_permissions.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddUserPermissions < ActiveRecord::Migration
   def up
     add_column :users, :can_create_group, :boolean, default: true, null: false
diff --git a/db/migrate/20130131070232_remove_private_flag_from_project.rb b/db/migrate/20130131070232_remove_private_flag_from_project.rb
index 5754db115589d6442a1c5a1bc9991b68c2b8937c..f0273ba448e90c4276fb65ce5b0e4bb5215f5847 100644
--- a/db/migrate/20130131070232_remove_private_flag_from_project.rb
+++ b/db/migrate/20130131070232_remove_private_flag_from_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemovePrivateFlagFromProject < ActiveRecord::Migration
   def up
     remove_column :projects, :private_flag
diff --git a/db/migrate/20130206084024_add_description_to_namsespace.rb b/db/migrate/20130206084024_add_description_to_namsespace.rb
index ef02e489d03088e33a910f487d8a6677f90575c8..62676ce8914ac04a06602e9c9279b1f2a220de4c 100644
--- a/db/migrate/20130206084024_add_description_to_namsespace.rb
+++ b/db/migrate/20130206084024_add_description_to_namsespace.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddDescriptionToNamsespace < ActiveRecord::Migration
   def change
     add_column :namespaces, :description, :string, default: '', null: false
diff --git a/db/migrate/20130207104426_add_description_to_teams.rb b/db/migrate/20130207104426_add_description_to_teams.rb
index 6d03777901c44333ba34990af2ecef9a62977813..bd9a4767b695f248a84577e11b4d21faec91ad23 100644
--- a/db/migrate/20130207104426_add_description_to_teams.rb
+++ b/db/migrate/20130207104426_add_description_to_teams.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddDescriptionToTeams < ActiveRecord::Migration
   def change
     add_column :user_teams, :description, :string, default: '', null: false
diff --git a/db/migrate/20130211085435_add_issues_tracker_id_to_project.rb b/db/migrate/20130211085435_add_issues_tracker_id_to_project.rb
index 71763d18aee5be037148417d05d72016dbd6c4ae..56b01cbf8921e321f3a2be00f815b1651185785f 100644
--- a/db/migrate/20130211085435_add_issues_tracker_id_to_project.rb
+++ b/db/migrate/20130211085435_add_issues_tracker_id_to_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddIssuesTrackerIdToProject < ActiveRecord::Migration
   def change
     add_column :projects, :issues_tracker_id, :string
diff --git a/db/migrate/20130214154045_rename_state_to_merge_status_in_milestone.rb b/db/migrate/20130214154045_rename_state_to_merge_status_in_milestone.rb
index 23797fe1894750acf664bb81c5be985b0071204f..4722cc13d4b5f7dd3f991d64e86518666ef915b0 100644
--- a/db/migrate/20130214154045_rename_state_to_merge_status_in_milestone.rb
+++ b/db/migrate/20130214154045_rename_state_to_merge_status_in_milestone.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RenameStateToMergeStatusInMilestone < ActiveRecord::Migration
   def change
     rename_column :merge_requests, :state, :merge_status
diff --git a/db/migrate/20130218140952_add_state_to_issue.rb b/db/migrate/20130218140952_add_state_to_issue.rb
index 062103d0e3389f948b3539bbfc79d6ad820b4351..3a5e978a1823ca66a78a9aafbb1b640c43ed9d50 100644
--- a/db/migrate/20130218140952_add_state_to_issue.rb
+++ b/db/migrate/20130218140952_add_state_to_issue.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddStateToIssue < ActiveRecord::Migration
   def change
     add_column :issues, :state, :string
diff --git a/db/migrate/20130218141038_add_state_to_merge_request.rb b/db/migrate/20130218141038_add_state_to_merge_request.rb
index ac4108ee311b5ef0854f2ee81ba1c026a0af8b38..e0180c755e2ad6df518536e6301e8ccf1d2fdea2 100644
--- a/db/migrate/20130218141038_add_state_to_merge_request.rb
+++ b/db/migrate/20130218141038_add_state_to_merge_request.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddStateToMergeRequest < ActiveRecord::Migration
   def change
     add_column :merge_requests, :state, :string
diff --git a/db/migrate/20130218141117_add_state_to_milestone.rb b/db/migrate/20130218141117_add_state_to_milestone.rb
index c84039106bd9064a3825a65d21fbec38e248eed7..5f71608692c452be6340128e4a9641c9554b61a1 100644
--- a/db/migrate/20130218141117_add_state_to_milestone.rb
+++ b/db/migrate/20130218141117_add_state_to_milestone.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddStateToMilestone < ActiveRecord::Migration
   def change
     add_column :milestones, :state, :string
diff --git a/db/migrate/20130218141258_convert_closed_to_state_in_issue.rb b/db/migrate/20130218141258_convert_closed_to_state_in_issue.rb
index 99289166e815dbf265c65d5441de72d989c91094..94c0a6845d586cfebe3d880bec92829c5d7ebb48 100644
--- a/db/migrate/20130218141258_convert_closed_to_state_in_issue.rb
+++ b/db/migrate/20130218141258_convert_closed_to_state_in_issue.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class ConvertClosedToStateInIssue < ActiveRecord::Migration
   include Gitlab::Database
 
diff --git a/db/migrate/20130218141327_convert_closed_to_state_in_merge_request.rb b/db/migrate/20130218141327_convert_closed_to_state_in_merge_request.rb
index bd1e016d6794bbcc2e4e04ca6760926897055696..64a9c761352dd1a9cd736a81cf84f124eae2b44e 100644
--- a/db/migrate/20130218141327_convert_closed_to_state_in_merge_request.rb
+++ b/db/migrate/20130218141327_convert_closed_to_state_in_merge_request.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class ConvertClosedToStateInMergeRequest < ActiveRecord::Migration
   include Gitlab::Database
 
diff --git a/db/migrate/20130218141344_convert_closed_to_state_in_milestone.rb b/db/migrate/20130218141344_convert_closed_to_state_in_milestone.rb
index d1174bc3d98e88422b447b1a56e53e63c58fb970..41508c2dc954a8ca3796f1a0c0ca16a96d97a5e2 100644
--- a/db/migrate/20130218141344_convert_closed_to_state_in_milestone.rb
+++ b/db/migrate/20130218141344_convert_closed_to_state_in_milestone.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class ConvertClosedToStateInMilestone < ActiveRecord::Migration
   include Gitlab::Database
 
diff --git a/db/migrate/20130218141444_remove_merged_from_merge_request.rb b/db/migrate/20130218141444_remove_merged_from_merge_request.rb
index a7bd82f500096e2d0a54080ff225cf636ff56532..afa5137061e9e8137dd186df61e68cd527bf9420 100644
--- a/db/migrate/20130218141444_remove_merged_from_merge_request.rb
+++ b/db/migrate/20130218141444_remove_merged_from_merge_request.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveMergedFromMergeRequest < ActiveRecord::Migration
   def up
     remove_column :merge_requests, :merged
diff --git a/db/migrate/20130218141507_remove_closed_from_issue.rb b/db/migrate/20130218141507_remove_closed_from_issue.rb
index 95cc064252b8f603b5e7bcb9365ae74ea82600c0..f250288bc3b61df0c8992fc83b9041bdbdccbb5f 100644
--- a/db/migrate/20130218141507_remove_closed_from_issue.rb
+++ b/db/migrate/20130218141507_remove_closed_from_issue.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveClosedFromIssue < ActiveRecord::Migration
   def up
     remove_column :issues, :closed
diff --git a/db/migrate/20130218141536_remove_closed_from_merge_request.rb b/db/migrate/20130218141536_remove_closed_from_merge_request.rb
index 371835938b213308efc9ef18bba15b1958fb4e66..efa12e326367d4aacb25053402c2c8d098f8e383 100644
--- a/db/migrate/20130218141536_remove_closed_from_merge_request.rb
+++ b/db/migrate/20130218141536_remove_closed_from_merge_request.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveClosedFromMergeRequest < ActiveRecord::Migration
   def up
     remove_column :merge_requests, :closed
diff --git a/db/migrate/20130218141554_remove_closed_from_milestone.rb b/db/migrate/20130218141554_remove_closed_from_milestone.rb
index e8dae4a19b1cb3fd235eb204ed46813e917c9810..75ac14e43bed3fdd62fdc7c5f2023015d8a1cc7b 100644
--- a/db/migrate/20130218141554_remove_closed_from_milestone.rb
+++ b/db/migrate/20130218141554_remove_closed_from_milestone.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveClosedFromMilestone < ActiveRecord::Migration
   def up
     remove_column :milestones, :closed
diff --git a/db/migrate/20130220124204_add_new_merge_status_to_merge_request.rb b/db/migrate/20130220124204_add_new_merge_status_to_merge_request.rb
index d78bd0ae923c2627496096e2de2fe775b845d74c..97615e47c89a3c199decaa6c833957012c5b11aa 100644
--- a/db/migrate/20130220124204_add_new_merge_status_to_merge_request.rb
+++ b/db/migrate/20130220124204_add_new_merge_status_to_merge_request.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddNewMergeStatusToMergeRequest < ActiveRecord::Migration
   def change
     add_column :merge_requests, :new_merge_status, :string
diff --git a/db/migrate/20130220125544_convert_merge_status_in_merge_request.rb b/db/migrate/20130220125544_convert_merge_status_in_merge_request.rb
index 1c758c56ffef0d2b3917486d3b8e37cfeaf4866b..3b8c3686c5525879114451b51d34e1b7177926d3 100644
--- a/db/migrate/20130220125544_convert_merge_status_in_merge_request.rb
+++ b/db/migrate/20130220125544_convert_merge_status_in_merge_request.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class ConvertMergeStatusInMergeRequest < ActiveRecord::Migration
   def up
     execute "UPDATE #{table_name} SET new_merge_status = 'unchecked' WHERE merge_status = 1"
diff --git a/db/migrate/20130220125545_remove_merge_status_from_merge_request.rb b/db/migrate/20130220125545_remove_merge_status_from_merge_request.rb
index 9083183beb0dfb818c5eedcfc540888762acf8c4..bd25ffbfc99561162a2c25bd270230e3cfe99474 100644
--- a/db/migrate/20130220125545_remove_merge_status_from_merge_request.rb
+++ b/db/migrate/20130220125545_remove_merge_status_from_merge_request.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveMergeStatusFromMergeRequest < ActiveRecord::Migration
   def up
     remove_column :merge_requests, :merge_status
diff --git a/db/migrate/20130220133245_rename_new_merge_status_to_merge_status_in_milestone.rb b/db/migrate/20130220133245_rename_new_merge_status_to_merge_status_in_milestone.rb
index 3f8f38dc979af8fe0556bd78b9befb5124e9766e..f0595720a3911dc5ccec5122039e0d0c1e7c6621 100644
--- a/db/migrate/20130220133245_rename_new_merge_status_to_merge_status_in_milestone.rb
+++ b/db/migrate/20130220133245_rename_new_merge_status_to_merge_status_in_milestone.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RenameNewMergeStatusToMergeStatusInMilestone < ActiveRecord::Migration
   def change
     rename_column :merge_requests, :new_merge_status, :merge_status
diff --git a/db/migrate/20130304104623_add_state_to_user.rb b/db/migrate/20130304104623_add_state_to_user.rb
index 8154c21065f711403a241d218916146dd6f5e146..4456d022e3f2eedc3f92f8183cd34148c65848b1 100644
--- a/db/migrate/20130304104623_add_state_to_user.rb
+++ b/db/migrate/20130304104623_add_state_to_user.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddStateToUser < ActiveRecord::Migration
   def change
     add_column :users, :state, :string
diff --git a/db/migrate/20130304104740_convert_blocked_to_state.rb b/db/migrate/20130304104740_convert_blocked_to_state.rb
index e8d5257ac96e88e49d3722d59dbf4c09ec2c87c9..9afd109364520073016a09b509528af7747b33dc 100644
--- a/db/migrate/20130304104740_convert_blocked_to_state.rb
+++ b/db/migrate/20130304104740_convert_blocked_to_state.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class ConvertBlockedToState < ActiveRecord::Migration
   def up
     User.transaction do
diff --git a/db/migrate/20130304105317_remove_blocked_from_user.rb b/db/migrate/20130304105317_remove_blocked_from_user.rb
index e010474538c5b22d76b7ec00ce030a3583b2a1b2..8f5b2c59b4396465abcbd598d0ab168a09357544 100644
--- a/db/migrate/20130304105317_remove_blocked_from_user.rb
+++ b/db/migrate/20130304105317_remove_blocked_from_user.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveBlockedFromUser < ActiveRecord::Migration
   def up
     remove_column :users, :blocked
diff --git a/db/migrate/20130315124931_user_color_scheme.rb b/db/migrate/20130315124931_user_color_scheme.rb
index 56c9a31ee3ca96984c74804a4fd25f4192df075e..06e28a49d9d7c0cac115dbf211e45301c617bb56 100644
--- a/db/migrate/20130315124931_user_color_scheme.rb
+++ b/db/migrate/20130315124931_user_color_scheme.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class UserColorScheme < ActiveRecord::Migration
   include Gitlab::Database
 
diff --git a/db/migrate/20130318212250_add_snippets_to_features.rb b/db/migrate/20130318212250_add_snippets_to_features.rb
index ad0b4434c435301911b86e22a58144e231c5838a..9860b85f504843128b2f95cba285920291f9886c 100644
--- a/db/migrate/20130318212250_add_snippets_to_features.rb
+++ b/db/migrate/20130318212250_add_snippets_to_features.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddSnippetsToFeatures < ActiveRecord::Migration
   def change
     add_column :projects, :snippets_enabled, :boolean, null: false, default: true
diff --git a/db/migrate/20130319214458_create_forked_project_links.rb b/db/migrate/20130319214458_create_forked_project_links.rb
index f91afc26e77c5424ecf9b299c11933827a4d676b..66eb11a4b2b77a185adb47f99094f8e8db0ef126 100644
--- a/db/migrate/20130319214458_create_forked_project_links.rb
+++ b/db/migrate/20130319214458_create_forked_project_links.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateForkedProjectLinks < ActiveRecord::Migration
   def change
     create_table :forked_project_links do |t|
diff --git a/db/migrate/20130323174317_add_private_to_snippets.rb b/db/migrate/20130323174317_add_private_to_snippets.rb
index 92f3a5c70118f7b4506aa0ac5d0fc63b3955bf78..376f4618d414aa61104a1c84762b47a7802c855b 100644
--- a/db/migrate/20130323174317_add_private_to_snippets.rb
+++ b/db/migrate/20130323174317_add_private_to_snippets.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddPrivateToSnippets < ActiveRecord::Migration
   def change
     add_column :snippets, :private, :boolean, null: false, default: true
diff --git a/db/migrate/20130324151736_add_type_to_snippets.rb b/db/migrate/20130324151736_add_type_to_snippets.rb
index 276aab2ca1578e77c1c73e50e685c40f427de2ec..097cb9bc7cb855f7b0ed1df4a3a7f7352c0eefc2 100644
--- a/db/migrate/20130324151736_add_type_to_snippets.rb
+++ b/db/migrate/20130324151736_add_type_to_snippets.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddTypeToSnippets < ActiveRecord::Migration
   def change
     add_column :snippets, :type, :string
diff --git a/db/migrate/20130324172327_change_project_id_to_null_in_snipepts.rb b/db/migrate/20130324172327_change_project_id_to_null_in_snipepts.rb
index 4c992bac4d114eb2220ffbf7daccc404ea5e7009..9256e62086e19841dba15ac648e588786ccd9305 100644
--- a/db/migrate/20130324172327_change_project_id_to_null_in_snipepts.rb
+++ b/db/migrate/20130324172327_change_project_id_to_null_in_snipepts.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class ChangeProjectIdToNullInSnipepts < ActiveRecord::Migration
   def up
     change_column :snippets, :project_id, :integer, :null => true
diff --git a/db/migrate/20130324203535_add_type_value_for_snippets.rb b/db/migrate/20130324203535_add_type_value_for_snippets.rb
index 8c05dd2cc717946e6352968e219cd121e458e439..6e910fd74c7b35bacca139a02ffa1270fab1dd63 100644
--- a/db/migrate/20130324203535_add_type_value_for_snippets.rb
+++ b/db/migrate/20130324203535_add_type_value_for_snippets.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddTypeValueForSnippets < ActiveRecord::Migration
   def up
     Snippet.where("project_id IS NOT NULL").update_all(type: 'ProjectSnippet')
diff --git a/db/migrate/20130325173941_add_notification_level_to_user.rb b/db/migrate/20130325173941_add_notification_level_to_user.rb
index 9f466e38c13648c80b884bf2225c976167bc6973..1dc58d4bcc89e22cafe736ff6dc46a52a4e24026 100644
--- a/db/migrate/20130325173941_add_notification_level_to_user.rb
+++ b/db/migrate/20130325173941_add_notification_level_to_user.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddNotificationLevelToUser < ActiveRecord::Migration
   def change
     add_column :users, :notification_level, :integer, null: false, default: 1
diff --git a/db/migrate/20130326142630_add_index_to_users_authentication_token.rb b/db/migrate/20130326142630_add_index_to_users_authentication_token.rb
index d42ef113738aad4a3bd4a9b4bf6d572b36199976..0592181927e73d13252c28e8e94173752b4d57d6 100644
--- a/db/migrate/20130326142630_add_index_to_users_authentication_token.rb
+++ b/db/migrate/20130326142630_add_index_to_users_authentication_token.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddIndexToUsersAuthenticationToken < ActiveRecord::Migration
   def change
     add_index :users, :authentication_token, unique: true
diff --git a/db/migrate/20130403003950_add_last_activity_column_into_project.rb b/db/migrate/20130403003950_add_last_activity_column_into_project.rb
index 85e31608d791fe7b7a6517605eccd51bdfc419a0..04a01612c6f75ff27e7ec887e1a5dd38ab036033 100644
--- a/db/migrate/20130403003950_add_last_activity_column_into_project.rb
+++ b/db/migrate/20130403003950_add_last_activity_column_into_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddLastActivityColumnIntoProject < ActiveRecord::Migration
   def up
     add_column :projects, :last_activity_at, :datetime
diff --git a/db/migrate/20130404164628_add_notification_level_to_user_project.rb b/db/migrate/20130404164628_add_notification_level_to_user_project.rb
index 27de5d6bf55704d138476271967ab6fc4a44d4cc..1e072d9c6e16752f54570feb15e5885b17a54952 100644
--- a/db/migrate/20130404164628_add_notification_level_to_user_project.rb
+++ b/db/migrate/20130404164628_add_notification_level_to_user_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddNotificationLevelToUserProject < ActiveRecord::Migration
   def change
     add_column :users_projects, :notification_level, :integer, null: false, default: 3
diff --git a/db/migrate/20130410175022_remove_wiki_table.rb b/db/migrate/20130410175022_remove_wiki_table.rb
index 9077aa2473c09da1085811efe110e169f5a85866..5885b1cc375b9dcf01c2df34e1292829a7409f9d 100644
--- a/db/migrate/20130410175022_remove_wiki_table.rb
+++ b/db/migrate/20130410175022_remove_wiki_table.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveWikiTable < ActiveRecord::Migration
   def up
     drop_table :wikis
diff --git a/db/migrate/20130419190306_allow_merges_for_forks.rb b/db/migrate/20130419190306_allow_merges_for_forks.rb
index 56ea97e856184a20fffc688f1bf5427eb043caee..ec953986c6affc8e47c00e111f0f6522165be78e 100644
--- a/db/migrate/20130419190306_allow_merges_for_forks.rb
+++ b/db/migrate/20130419190306_allow_merges_for_forks.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AllowMergesForForks < ActiveRecord::Migration
   def self.up
     add_column :merge_requests, :target_project_id, :integer, :null => true
diff --git a/db/migrate/20130506085413_add_type_to_key.rb b/db/migrate/20130506085413_add_type_to_key.rb
index 315e7ca77b3c2d7b5cc62260e7eb7404f069b7f1..c9f1ee4e3898c4d2d71ba09772b2bc53b8caba78 100644
--- a/db/migrate/20130506085413_add_type_to_key.rb
+++ b/db/migrate/20130506085413_add_type_to_key.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddTypeToKey < ActiveRecord::Migration
   def change
     add_column :keys, :type, :string
diff --git a/db/migrate/20130506090604_create_deploy_keys_projects.rb b/db/migrate/20130506090604_create_deploy_keys_projects.rb
index 0dc8cdeb07dc4805a4eda6d1c777c226c6434306..7d6662d358ad4c70ef1088c96a7557d915f80d7a 100644
--- a/db/migrate/20130506090604_create_deploy_keys_projects.rb
+++ b/db/migrate/20130506090604_create_deploy_keys_projects.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateDeployKeysProjects < ActiveRecord::Migration
   def change
     create_table :deploy_keys_projects do |t|
diff --git a/db/migrate/20130506095501_remove_project_id_from_key.rb b/db/migrate/20130506095501_remove_project_id_from_key.rb
index 6b794cfb5c133e7d717824c3c592a95ca9130bed..53abc4e7b52bc29744c4af2d5200293ae95eac6a 100644
--- a/db/migrate/20130506095501_remove_project_id_from_key.rb
+++ b/db/migrate/20130506095501_remove_project_id_from_key.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveProjectIdFromKey < ActiveRecord::Migration
   def up
     puts 'Migrate deploy keys: '
diff --git a/db/migrate/20130522141856_add_more_fields_to_service.rb b/db/migrate/20130522141856_add_more_fields_to_service.rb
index 298e902df2f9284701c4260db1ac92c6706071fa..9f764a1d050992e89c38342ef695da8592681b63 100644
--- a/db/migrate/20130522141856_add_more_fields_to_service.rb
+++ b/db/migrate/20130522141856_add_more_fields_to_service.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddMoreFieldsToService < ActiveRecord::Migration
   def change
     add_column :services, :subdomain, :string
diff --git a/db/migrate/20130528184641_add_system_to_notes.rb b/db/migrate/20130528184641_add_system_to_notes.rb
index 1b22a4934f992d8c4ae109456a1ab4f6d014a12b..27fbf8983ac95e90fdfeac31f19a212748befb63 100644
--- a/db/migrate/20130528184641_add_system_to_notes.rb
+++ b/db/migrate/20130528184641_add_system_to_notes.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddSystemToNotes < ActiveRecord::Migration
   class Note < ActiveRecord::Base
   end
diff --git a/db/migrate/20130611210815_increase_snippet_text_column_size.rb b/db/migrate/20130611210815_increase_snippet_text_column_size.rb
index f7b4447e43e8ba8348fcc5e6617126454bcf851c..f710c79a9a5d0fedc861904c82ea58f273613fa4 100644
--- a/db/migrate/20130611210815_increase_snippet_text_column_size.rb
+++ b/db/migrate/20130611210815_increase_snippet_text_column_size.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class IncreaseSnippetTextColumnSize < ActiveRecord::Migration
   def up
     # MYSQL LARGETEXT for snippet
diff --git a/db/migrate/20130613165816_add_password_expires_at_to_users.rb b/db/migrate/20130613165816_add_password_expires_at_to_users.rb
index 3479c8e64d015209582639749271400e0ab873a9..47306a370a8d1b1d0ff0e92b24c045cddafb7a8c 100644
--- a/db/migrate/20130613165816_add_password_expires_at_to_users.rb
+++ b/db/migrate/20130613165816_add_password_expires_at_to_users.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddPasswordExpiresAtToUsers < ActiveRecord::Migration
   def change
     add_column :users, :password_expires_at, :datetime
diff --git a/db/migrate/20130613173246_add_created_by_id_to_user.rb b/db/migrate/20130613173246_add_created_by_id_to_user.rb
index 615e96eb156d8f58d20eb20d61b85b670325fa98..3138c0f40a79f713340f0926c23245d8ae6bdaaa 100644
--- a/db/migrate/20130613173246_add_created_by_id_to_user.rb
+++ b/db/migrate/20130613173246_add_created_by_id_to_user.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddCreatedByIdToUser < ActiveRecord::Migration
   def change
     add_column :users, :created_by_id, :integer
diff --git a/db/migrate/20130614132337_add_improted_to_project.rb b/db/migrate/20130614132337_add_improted_to_project.rb
index cc882c3f10a596cae19af15cd8514958b6c40d30..26dc16e3b43124140fcd5d0bb4d8ceae74cb2776 100644
--- a/db/migrate/20130614132337_add_improted_to_project.rb
+++ b/db/migrate/20130614132337_add_improted_to_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddImprotedToProject < ActiveRecord::Migration
   def change
     add_column :projects, :imported, :boolean, default: false, null: false
diff --git a/db/migrate/20130617095603_create_users_groups.rb b/db/migrate/20130617095603_create_users_groups.rb
index 2efc04f1151e04ffa5f80f8952113d9ec73a749a..45cff93fe4ad9051f42887dd9b1743f53092fd86 100644
--- a/db/migrate/20130617095603_create_users_groups.rb
+++ b/db/migrate/20130617095603_create_users_groups.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateUsersGroups < ActiveRecord::Migration
   def change
     create_table :users_groups do |t|
diff --git a/db/migrate/20130621195223_add_notification_level_to_user_group.rb b/db/migrate/20130621195223_add_notification_level_to_user_group.rb
index 8c2e3dfcaca2e28dd8ba0503b8ab66ec040accc8..6fd4941f615369a9ff649bfc237b18a18e8f6ac7 100644
--- a/db/migrate/20130621195223_add_notification_level_to_user_group.rb
+++ b/db/migrate/20130621195223_add_notification_level_to_user_group.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddNotificationLevelToUserGroup < ActiveRecord::Migration
   def change
     add_column :users_groups, :notification_level, :integer, null: false, default: 3
diff --git a/db/migrate/20130622115340_add_more_db_index.rb b/db/migrate/20130622115340_add_more_db_index.rb
index 9570a7a3f1e60add51ab296789059a2ccc88188e..4113217de59656d49d266fcf8e87b7c5716246d0 100644
--- a/db/migrate/20130622115340_add_more_db_index.rb
+++ b/db/migrate/20130622115340_add_more_db_index.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddMoreDbIndex < ActiveRecord::Migration
   def change
     add_index :deploy_keys_projects, :project_id
diff --git a/db/migrate/20130624162710_add_fingerprint_to_key.rb b/db/migrate/20130624162710_add_fingerprint_to_key.rb
index 544a83667275043c851b3c05d5a99cbb2f0369e3..3e574ea81b9755b06efdd0ed05f6b19545d52405 100644
--- a/db/migrate/20130624162710_add_fingerprint_to_key.rb
+++ b/db/migrate/20130624162710_add_fingerprint_to_key.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddFingerprintToKey < ActiveRecord::Migration
   def change
     add_column :keys, :fingerprint, :string
diff --git a/db/migrate/20130711063759_create_project_group_links.rb b/db/migrate/20130711063759_create_project_group_links.rb
index 395083f2a03c9169a84060f633d22f608dd00e73..bd9d40a50db7c28e1b3d9700132c189d8371b79e 100644
--- a/db/migrate/20130711063759_create_project_group_links.rb
+++ b/db/migrate/20130711063759_create_project_group_links.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateProjectGroupLinks < ActiveRecord::Migration
   def change
     create_table :project_group_links do |t|
diff --git a/db/migrate/20130804151314_add_st_diff_to_note.rb b/db/migrate/20130804151314_add_st_diff_to_note.rb
index 3f9abb975c387ffdd504d2c348f053c9eca2aa24..9e2da73b695c56ec442660f254c126f8c57e7360 100644
--- a/db/migrate/20130804151314_add_st_diff_to_note.rb
+++ b/db/migrate/20130804151314_add_st_diff_to_note.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddStDiffToNote < ActiveRecord::Migration
   def change
     add_column :notes, :st_diff, :text, :null => true
diff --git a/db/migrate/20130809124851_add_permission_check_to_user.rb b/db/migrate/20130809124851_add_permission_check_to_user.rb
index c26157904c7348cb193810a61bc5f7e6fa30a2a9..9f9dea36101195a4ddb08e52e87add68d4dac489 100644
--- a/db/migrate/20130809124851_add_permission_check_to_user.rb
+++ b/db/migrate/20130809124851_add_permission_check_to_user.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddPermissionCheckToUser < ActiveRecord::Migration
   def change
     add_column :users, :last_credential_check_at, :datetime
diff --git a/db/migrate/20130812143708_add_import_url_to_project.rb b/db/migrate/20130812143708_add_import_url_to_project.rb
index 023a48741b263204067297495382f1b6d6fbcea5..d2bdfe1894ed5b1cb5ed279ef3565b0ab17ef1a3 100644
--- a/db/migrate/20130812143708_add_import_url_to_project.rb
+++ b/db/migrate/20130812143708_add_import_url_to_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddImportUrlToProject < ActiveRecord::Migration
   def change
     add_column :projects, :import_url, :string
diff --git a/db/migrate/20130819182730_add_internal_ids_to_issues_and_mr.rb b/db/migrate/20130819182730_add_internal_ids_to_issues_and_mr.rb
index e55ae38f144d5f4f181fa5a9b287834ec73f00fb..0e0e78b0f0d28fa53507fca50a610a43044ba7ed 100644
--- a/db/migrate/20130819182730_add_internal_ids_to_issues_and_mr.rb
+++ b/db/migrate/20130819182730_add_internal_ids_to_issues_and_mr.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddInternalIdsToIssuesAndMr < ActiveRecord::Migration
   def change
     add_column :issues, :iid, :integer
diff --git a/db/migrate/20130820102832_add_access_to_project_group_link.rb b/db/migrate/20130820102832_add_access_to_project_group_link.rb
index 00e3947a6bbd145d9c0c8286536475ab47bf3936..98f3fa8752379e3b0ae3ebc1f76dad0997cabe61 100644
--- a/db/migrate/20130820102832_add_access_to_project_group_link.rb
+++ b/db/migrate/20130820102832_add_access_to_project_group_link.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddAccessToProjectGroupLink < ActiveRecord::Migration
   def change
     add_column :project_group_links, :group_access, :integer, null: false, default: ProjectGroupLink.default_access
diff --git a/db/migrate/20130821090530_remove_deprecated_tables.rb b/db/migrate/20130821090530_remove_deprecated_tables.rb
index 539c0617eeb7747e851da95d6ba6b5b96934c73f..d22e713a7a114893c991475ef96e93970ad7d1e4 100644
--- a/db/migrate/20130821090530_remove_deprecated_tables.rb
+++ b/db/migrate/20130821090530_remove_deprecated_tables.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveDeprecatedTables < ActiveRecord::Migration
   def up
     drop_table :user_teams
diff --git a/db/migrate/20130821090531_add_internal_ids_to_milestones.rb b/db/migrate/20130821090531_add_internal_ids_to_milestones.rb
index 33e5bae580552a984fbb84e91ef94b1a3f4003f3..e25b8f9166266eb5a465a9c81814cefb83555334 100644
--- a/db/migrate/20130821090531_add_internal_ids_to_milestones.rb
+++ b/db/migrate/20130821090531_add_internal_ids_to_milestones.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddInternalIdsToMilestones < ActiveRecord::Migration
   def change
     add_column :milestones, :iid, :integer
diff --git a/db/migrate/20130909132950_add_description_to_merge_request.rb b/db/migrate/20130909132950_add_description_to_merge_request.rb
index 9bcd0c7ee0689122e979b5ea63f7b04a0348f768..fbac50c8216096de8f5769222d57c1fc476e109f 100644
--- a/db/migrate/20130909132950_add_description_to_merge_request.rb
+++ b/db/migrate/20130909132950_add_description_to_merge_request.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddDescriptionToMergeRequest < ActiveRecord::Migration
   def change
     add_column :merge_requests, :description, :text, null: true
diff --git a/db/migrate/20130926081215_change_owner_id_for_group.rb b/db/migrate/20130926081215_change_owner_id_for_group.rb
index 8f1992c37ab0bb51c4b54c0e8b82da7798fce568..2bdd22d5a04094f3493b3702e04ff2547b9b2471 100644
--- a/db/migrate/20130926081215_change_owner_id_for_group.rb
+++ b/db/migrate/20130926081215_change_owner_id_for_group.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class ChangeOwnerIdForGroup < ActiveRecord::Migration
   def up
     change_column :namespaces, :owner_id, :integer, null: true
diff --git a/db/migrate/20131005191208_add_avatar_to_users.rb b/db/migrate/20131005191208_add_avatar_to_users.rb
index 7b4de37ad72369481793ec72a1ce99ec867f21ba..df9057b81d6b573c311a2f6aca9a4398e6d0f958 100644
--- a/db/migrate/20131005191208_add_avatar_to_users.rb
+++ b/db/migrate/20131005191208_add_avatar_to_users.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddAvatarToUsers < ActiveRecord::Migration
   def change
     add_column :users, :avatar, :string
diff --git a/db/migrate/20131009115346_add_confirmable_to_users.rb b/db/migrate/20131009115346_add_confirmable_to_users.rb
index 249cbe704ed678596bee82a744218c97ee4253dd..d714dd98e854edae50de022dfaf6d82fa5b45b36 100644
--- a/db/migrate/20131009115346_add_confirmable_to_users.rb
+++ b/db/migrate/20131009115346_add_confirmable_to_users.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddConfirmableToUsers < ActiveRecord::Migration
   def self.up
     add_column :users, :confirmation_token, :string
diff --git a/db/migrate/20131106151520_remove_default_branch.rb b/db/migrate/20131106151520_remove_default_branch.rb
index 88a890eb3eb7e759ced674d594f3218d57a91efe..fd3d1ed7ab32ac71ddabd4c2e2d23789c3b72379 100644
--- a/db/migrate/20131106151520_remove_default_branch.rb
+++ b/db/migrate/20131106151520_remove_default_branch.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveDefaultBranch < ActiveRecord::Migration
   def up
     remove_column :projects, :default_branch
diff --git a/db/migrate/20131112114325_create_broadcast_messages.rb b/db/migrate/20131112114325_create_broadcast_messages.rb
index 147178e9dcf2dc7d490f82507aa6fe144f449cf3..ce37a8e2708655dbd3da66ff486318f5fbd2ee21 100644
--- a/db/migrate/20131112114325_create_broadcast_messages.rb
+++ b/db/migrate/20131112114325_create_broadcast_messages.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateBroadcastMessages < ActiveRecord::Migration
   def change
     create_table :broadcast_messages do |t|
diff --git a/db/migrate/20131112220935_add_visibility_level_to_projects.rb b/db/migrate/20131112220935_add_visibility_level_to_projects.rb
index 89421cbedaddc00641710b50cdcfb7e9bc9314cd..5efc17b228e91457cbe576c5004d8567826a100b 100644
--- a/db/migrate/20131112220935_add_visibility_level_to_projects.rb
+++ b/db/migrate/20131112220935_add_visibility_level_to_projects.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddVisibilityLevelToProjects < ActiveRecord::Migration
   include Gitlab::Database
 
diff --git a/db/migrate/20131129154016_add_archived_to_projects.rb b/db/migrate/20131129154016_add_archived_to_projects.rb
index 917e690ba477d552aa6cbbc79ffdd490441c87bd..e8e6908d137f4a81692083c648498b3b7d851657 100644
--- a/db/migrate/20131129154016_add_archived_to_projects.rb
+++ b/db/migrate/20131129154016_add_archived_to_projects.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddArchivedToProjects < ActiveRecord::Migration
   def change
     add_column :projects, :archived, :boolean, default: false, null: false
diff --git a/db/migrate/20131130165425_add_color_and_font_to_broadcast_messages.rb b/db/migrate/20131130165425_add_color_and_font_to_broadcast_messages.rb
index 473f355eceb0566f4caf97ce575770d95dc5e690..348a284a53e8771fbccd6ecad14079da5f6f63dc 100644
--- a/db/migrate/20131130165425_add_color_and_font_to_broadcast_messages.rb
+++ b/db/migrate/20131130165425_add_color_and_font_to_broadcast_messages.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddColorAndFontToBroadcastMessages < ActiveRecord::Migration
   def change
     add_column :broadcast_messages, :color, :string
diff --git a/db/migrate/20131202192556_add_event_fields_for_web_hook.rb b/db/migrate/20131202192556_add_event_fields_for_web_hook.rb
index d29e996852ec3e9a1f8e8f215a9ecfd74ec421fa..99d76611524b835cdb5470902195f8dca557ce95 100644
--- a/db/migrate/20131202192556_add_event_fields_for_web_hook.rb
+++ b/db/migrate/20131202192556_add_event_fields_for_web_hook.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddEventFieldsForWebHook < ActiveRecord::Migration
   def change
     add_column :web_hooks, :push_events, :boolean, default: true, null: false
diff --git a/db/migrate/20131214224427_add_hide_no_ssh_key_to_users.rb b/db/migrate/20131214224427_add_hide_no_ssh_key_to_users.rb
index 7cec79e7ee823547e9b6c026198b0c97581e3d84..4333dc5932325e96ce2469974f04125f2c249227 100644
--- a/db/migrate/20131214224427_add_hide_no_ssh_key_to_users.rb
+++ b/db/migrate/20131214224427_add_hide_no_ssh_key_to_users.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddHideNoSshKeyToUsers < ActiveRecord::Migration
   def change
     add_column :users, :hide_no_ssh_key, :boolean, :default => false
diff --git a/db/migrate/20131217102743_add_recipients_to_service.rb b/db/migrate/20131217102743_add_recipients_to_service.rb
index 9695c25135202edacea014e237179a4fb1ee62fa..3c76be0f68d444fd6b513091580463288b39d1f0 100644
--- a/db/migrate/20131217102743_add_recipients_to_service.rb
+++ b/db/migrate/20131217102743_add_recipients_to_service.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddRecipientsToService < ActiveRecord::Migration
   def change
     add_column :services, :recipients, :text
diff --git a/db/migrate/20140116231608_add_website_url_to_users.rb b/db/migrate/20140116231608_add_website_url_to_users.rb
index 0996fdcad73257dc31b728a2da9564b4715626c2..1c39423562e2dee3ce05d081bc7bc57aaef79a93 100644
--- a/db/migrate/20140116231608_add_website_url_to_users.rb
+++ b/db/migrate/20140116231608_add_website_url_to_users.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddWebsiteUrlToUsers < ActiveRecord::Migration
   def change
     add_column :users, :website_url, :string, {:null => false, :default => ''}
diff --git a/db/migrate/20140122112253_create_merge_request_diffs.rb b/db/migrate/20140122112253_create_merge_request_diffs.rb
index f34e30925dfd3541af78e7a41fb8849845be2e6a..395c3edfc7972a260e895852016dd48950bff35d 100644
--- a/db/migrate/20140122112253_create_merge_request_diffs.rb
+++ b/db/migrate/20140122112253_create_merge_request_diffs.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateMergeRequestDiffs < ActiveRecord::Migration
   def up
     create_table :merge_request_diffs do |t|
diff --git a/db/migrate/20140122114406_migrate_mr_diffs.rb b/db/migrate/20140122114406_migrate_mr_diffs.rb
index 1595e2b64725923ee02c0f65a65d25144f573f47..429aeb2293f3a82002c50d2d92d94d97bbbcb977 100644
--- a/db/migrate/20140122114406_migrate_mr_diffs.rb
+++ b/db/migrate/20140122114406_migrate_mr_diffs.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class MigrateMrDiffs < ActiveRecord::Migration
   def self.up
     execute "INSERT INTO merge_request_diffs ( merge_request_id, st_commits, st_diffs ) SELECT id, st_commits, st_diffs FROM merge_requests"
diff --git a/db/migrate/20140122122549_remove_m_rdiff_fields.rb b/db/migrate/20140122122549_remove_m_rdiff_fields.rb
index 8f863d85a684909d886a7ee67685b28aca190749..bbf35811b61e637afe58d63428eb623ecaea0d0c 100644
--- a/db/migrate/20140122122549_remove_m_rdiff_fields.rb
+++ b/db/migrate/20140122122549_remove_m_rdiff_fields.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveMRdiffFields < ActiveRecord::Migration
   def up
     remove_column :merge_requests, :st_commits
diff --git a/db/migrate/20140125162722_add_avatar_to_projects.rb b/db/migrate/20140125162722_add_avatar_to_projects.rb
index 9523ac722f2fa2144759e3cd268374eb1747e322..888341b753508a55387b48413319b92f987b2762 100644
--- a/db/migrate/20140125162722_add_avatar_to_projects.rb
+++ b/db/migrate/20140125162722_add_avatar_to_projects.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddAvatarToProjects < ActiveRecord::Migration
   def change
     add_column :projects, :avatar, :string
diff --git a/db/migrate/20140127170938_add_group_avatars.rb b/db/migrate/20140127170938_add_group_avatars.rb
index 2911096dd5d300268d1efeedb3510a368d5bc8a3..95d1c1c6b278f3f7f7f70453b19a19452fc9e1f4 100644
--- a/db/migrate/20140127170938_add_group_avatars.rb
+++ b/db/migrate/20140127170938_add_group_avatars.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddGroupAvatars < ActiveRecord::Migration
   def change
     add_column :namespaces, :avatar, :string
diff --git a/db/migrate/20140209025651_create_emails.rb b/db/migrate/20140209025651_create_emails.rb
index cb78c4af11b25b59533c2d15a33292c01920eb3d..571beb19cdd6e111bb20c796da134514a2fd88e6 100644
--- a/db/migrate/20140209025651_create_emails.rb
+++ b/db/migrate/20140209025651_create_emails.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateEmails < ActiveRecord::Migration
   def change
     create_table :emails do |t|
diff --git a/db/migrate/20140214102325_add_api_key_to_services.rb b/db/migrate/20140214102325_add_api_key_to_services.rb
index 30eeca2c1f65c05999eaad5f65d6b4e0b2393d9e..b58c36c0a30e239da801d60e1a522ffd418ecd2d 100644
--- a/db/migrate/20140214102325_add_api_key_to_services.rb
+++ b/db/migrate/20140214102325_add_api_key_to_services.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddApiKeyToServices < ActiveRecord::Migration
   def change
     add_column :services, :api_key, :string
diff --git a/db/migrate/20140304005354_add_index_merge_request_diffs_on_merge_request_id.rb b/db/migrate/20140304005354_add_index_merge_request_diffs_on_merge_request_id.rb
index 65d28e8cb01f1afeb4bed9ffe660bf48e90f6f48..aab8a41c2c3545015945b0195258aa47b6a35402 100644
--- a/db/migrate/20140304005354_add_index_merge_request_diffs_on_merge_request_id.rb
+++ b/db/migrate/20140304005354_add_index_merge_request_diffs_on_merge_request_id.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddIndexMergeRequestDiffsOnMergeRequestId < ActiveRecord::Migration
   def change
     add_index :merge_request_diffs, :merge_request_id, unique: true
diff --git a/db/migrate/20140305193308_add_tag_push_hooks_to_project_hook.rb b/db/migrate/20140305193308_add_tag_push_hooks_to_project_hook.rb
index 7017148702a4013e27b5627865454fe18fff292c..ec163bb843c6d021eb6306a7d4d1e12ade6cd08b 100644
--- a/db/migrate/20140305193308_add_tag_push_hooks_to_project_hook.rb
+++ b/db/migrate/20140305193308_add_tag_push_hooks_to_project_hook.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddTagPushHooksToProjectHook < ActiveRecord::Migration
   def change
     add_column :web_hooks, :tag_push_events, :boolean, default: false
diff --git a/db/migrate/20140312145357_add_import_status_to_project.rb b/db/migrate/20140312145357_add_import_status_to_project.rb
index ef972e8342a0e4f0105dc8aabc5f98d4f1e344b2..9947cd8c6f9c37ac89521f529f9cd797058bc68f 100644
--- a/db/migrate/20140312145357_add_import_status_to_project.rb
+++ b/db/migrate/20140312145357_add_import_status_to_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddImportStatusToProject < ActiveRecord::Migration
   def change
     add_column :projects, :import_status, :string
diff --git a/db/migrate/20140313092127_migrate_already_imported_projects.rb b/db/migrate/20140313092127_migrate_already_imported_projects.rb
index 0a9f73a5758f7f647dee254264cb0c0f4e539f7a..f2e91fe1b409b457425a727fadde1fa80a73f964 100644
--- a/db/migrate/20140313092127_migrate_already_imported_projects.rb
+++ b/db/migrate/20140313092127_migrate_already_imported_projects.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class MigrateAlreadyImportedProjects < ActiveRecord::Migration
   include Gitlab::Database
 
diff --git a/db/migrate/20140407135544_fix_namespaces.rb b/db/migrate/20140407135544_fix_namespaces.rb
index 59665d538f00917d2a885f70801cda1683a8a930..9137496669837f189ae1ccc2c04b89e09e691350 100644
--- a/db/migrate/20140407135544_fix_namespaces.rb
+++ b/db/migrate/20140407135544_fix_namespaces.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class FixNamespaces < ActiveRecord::Migration
   def up
     Namespace.where('name <> path and type is null').each do |namespace|
diff --git a/db/migrate/20140414131055_change_state_to_allow_empty_merge_request_diffs.rb b/db/migrate/20140414131055_change_state_to_allow_empty_merge_request_diffs.rb
index 1f6d85d5f66ce0d0773d4bac461b0dcf50fe5310..fb9c7a6636e8fedf89656a9728bcf7293fcffdf2 100644
--- a/db/migrate/20140414131055_change_state_to_allow_empty_merge_request_diffs.rb
+++ b/db/migrate/20140414131055_change_state_to_allow_empty_merge_request_diffs.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class ChangeStateToAllowEmptyMergeRequestDiffs < ActiveRecord::Migration
   def up
     change_column :merge_request_diffs, :state, :string, null: true,
diff --git a/db/migrate/20140415124820_limits_to_mysql.rb b/db/migrate/20140415124820_limits_to_mysql.rb
index 3f6e62617c57ab82367fafd407c89815c504c34d..c712423bcd1c2afa5e8f9d610db01eeca8cca829 100644
--- a/db/migrate/20140415124820_limits_to_mysql.rb
+++ b/db/migrate/20140415124820_limits_to_mysql.rb
@@ -1 +1,2 @@
+# rubocop:disable all
 require_relative 'limits_to_mysql'
diff --git a/db/migrate/20140416074002_add_index_on_iid.rb b/db/migrate/20140416074002_add_index_on_iid.rb
index 85269e2a03b3be385cdca8d69d0b0635b06ca73e..6cdaa5a3c08e3c3479d2810bd4b0491df6f17850 100644
--- a/db/migrate/20140416074002_add_index_on_iid.rb
+++ b/db/migrate/20140416074002_add_index_on_iid.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddIndexOnIid < ActiveRecord::Migration
   def change
     RemoveDuplicateIid.clean(Issue)
diff --git a/db/migrate/20140416185734_index_on_current_sign_in_at.rb b/db/migrate/20140416185734_index_on_current_sign_in_at.rb
index 0bf80ce154a151366967d45921bcebef5bcbe143..8c620b545bd5c2e758a4df25c2e94127c412c7a4 100644
--- a/db/migrate/20140416185734_index_on_current_sign_in_at.rb
+++ b/db/migrate/20140416185734_index_on_current_sign_in_at.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class IndexOnCurrentSignInAt < ActiveRecord::Migration
   def change
     add_index :users, :current_sign_in_at
diff --git a/db/migrate/20140428105831_add_notes_index_updated_at.rb b/db/migrate/20140428105831_add_notes_index_updated_at.rb
index 6c25570f128141919bee9e459d9690a4b63cb445..0589101af9385888703332b4b4675e6c11bba1b2 100644
--- a/db/migrate/20140428105831_add_notes_index_updated_at.rb
+++ b/db/migrate/20140428105831_add_notes_index_updated_at.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddNotesIndexUpdatedAt < ActiveRecord::Migration
   def change
     add_index :notes, :updated_at
diff --git a/db/migrate/20140502115131_add_repo_size_to_db.rb b/db/migrate/20140502115131_add_repo_size_to_db.rb
index 7361d1a9440f3592bdff0d3235df55a365ebd115..090b30a4f26938398537e1bb78c0f10f13adc629 100644
--- a/db/migrate/20140502115131_add_repo_size_to_db.rb
+++ b/db/migrate/20140502115131_add_repo_size_to_db.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddRepoSizeToDb < ActiveRecord::Migration
   def change
     add_column :projects, :repository_size, :float, default: 0
diff --git a/db/migrate/20140502125220_migrate_repo_size.rb b/db/migrate/20140502125220_migrate_repo_size.rb
index efdf53112fd491f4350328705ade33cb36a4417d..84463727b3b8a7cdf925594384c87361a488153c 100644
--- a/db/migrate/20140502125220_migrate_repo_size.rb
+++ b/db/migrate/20140502125220_migrate_repo_size.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class MigrateRepoSize < ActiveRecord::Migration
   def up
     project_data = execute('SELECT projects.id, namespaces.path AS namespace_path, projects.path AS project_path FROM projects LEFT JOIN namespaces ON projects.namespace_id = namespaces.id')
diff --git a/db/migrate/20140611135229_add_position_to_merge_request.rb b/db/migrate/20140611135229_add_position_to_merge_request.rb
index d5fdecd0c39a04ba11739894974ffa9051b9e5ff..3a7d2f7c359be201075cff5e7179f5a7d1c9d245 100644
--- a/db/migrate/20140611135229_add_position_to_merge_request.rb
+++ b/db/migrate/20140611135229_add_position_to_merge_request.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddPositionToMergeRequest < ActiveRecord::Migration
   def change
     add_column :merge_requests, :position, :integer, default: 0
diff --git a/db/migrate/20140625115202_create_users_star_projects.rb b/db/migrate/20140625115202_create_users_star_projects.rb
index 412f0f6f34be75240d3f66552239a9c04d596f2c..32dd99e83be5eb2d1eee63bddec8e681afac8608 100644
--- a/db/migrate/20140625115202_create_users_star_projects.rb
+++ b/db/migrate/20140625115202_create_users_star_projects.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateUsersStarProjects < ActiveRecord::Migration
   def change
     create_table :users_star_projects do |t|
diff --git a/db/migrate/20140729134820_create_labels.rb b/db/migrate/20140729134820_create_labels.rb
index 3a4b6a152dc2f9a1c61401336692073b66d54376..df0f8cb9f0308725538cf7679b316f144abd9efb 100644
--- a/db/migrate/20140729134820_create_labels.rb
+++ b/db/migrate/20140729134820_create_labels.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateLabels < ActiveRecord::Migration
   def change
     create_table :labels do |t|
diff --git a/db/migrate/20140729140420_create_label_links.rb b/db/migrate/20140729140420_create_label_links.rb
index 2bfc4ae2094628d0d507d41824501183ed92ebfa..fa5992605f81bff2bf0df5c9118390c9c3b0f855 100644
--- a/db/migrate/20140729140420_create_label_links.rb
+++ b/db/migrate/20140729140420_create_label_links.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateLabelLinks < ActiveRecord::Migration
   def change
     create_table :label_links do |t|
diff --git a/db/migrate/20140729145339_migrate_project_tags.rb b/db/migrate/20140729145339_migrate_project_tags.rb
index 5760e4bfeaa85054dc04c24741d60f588b972a27..ac46847f3e63a3e156fab2ac0f70dcd594b27331 100644
--- a/db/migrate/20140729145339_migrate_project_tags.rb
+++ b/db/migrate/20140729145339_migrate_project_tags.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class MigrateProjectTags < ActiveRecord::Migration
   def up
     ActsAsTaggableOn::Tagging.where(taggable_type: 'Project', context: 'labels').update_all(context: 'tags')
diff --git a/db/migrate/20140729152420_migrate_taggable_labels.rb b/db/migrate/20140729152420_migrate_taggable_labels.rb
index dc28d727d9a450ad9bffcedc256f062363870d19..04cdc6beaddcad8e88fc07db03890bb2dbfce565 100644
--- a/db/migrate/20140729152420_migrate_taggable_labels.rb
+++ b/db/migrate/20140729152420_migrate_taggable_labels.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class MigrateTaggableLabels < ActiveRecord::Migration
   def up
     taggings = ActsAsTaggableOn::Tagging.where(taggable_type: ['Issue', 'MergeRequest'], context: 'labels')
diff --git a/db/migrate/20140730111702_add_index_to_labels.rb b/db/migrate/20140730111702_add_index_to_labels.rb
index 494241c873cbd742272c3b94e3b192da94d3ce60..cc7ac1fc449cf3fa27112936f6cf1386b562865d 100644
--- a/db/migrate/20140730111702_add_index_to_labels.rb
+++ b/db/migrate/20140730111702_add_index_to_labels.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddIndexToLabels < ActiveRecord::Migration
   def change
     add_index "labels", :project_id
diff --git a/db/migrate/20140903115954_migrate_to_new_shell.rb b/db/migrate/20140903115954_migrate_to_new_shell.rb
index 54cbe48960a5a791d096e3fbfcd205d50fc637d0..04acf24284b77b68ef81cdde0c3abfdce9cf496c 100644
--- a/db/migrate/20140903115954_migrate_to_new_shell.rb
+++ b/db/migrate/20140903115954_migrate_to_new_shell.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class MigrateToNewShell < ActiveRecord::Migration
   def change
     return if Rails.env.test?
diff --git a/db/migrate/20140907220153_serialize_service_properties.rb b/db/migrate/20140907220153_serialize_service_properties.rb
index d45a10465be7ad8ef62a537b8c2128d67c91eb7b..c2d67fad0abe2f8c387f266075abbcb4e6dedfca 100644
--- a/db/migrate/20140907220153_serialize_service_properties.rb
+++ b/db/migrate/20140907220153_serialize_service_properties.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class SerializeServiceProperties < ActiveRecord::Migration
   def change
     unless column_exists?(:services, :properties)
diff --git a/db/migrate/20140914113604_add_members_table.rb b/db/migrate/20140914113604_add_members_table.rb
index d311f3033ee65d75156391b27cfecffb6a02785e..bc3c1bb61e4182a2e7a5d20e29d28df2ff91ac1b 100644
--- a/db/migrate/20140914113604_add_members_table.rb
+++ b/db/migrate/20140914113604_add_members_table.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddMembersTable < ActiveRecord::Migration
   def change
     create_table :members do |t|
diff --git a/db/migrate/20140914145549_migrate_to_new_members_model.rb b/db/migrate/20140914145549_migrate_to_new_members_model.rb
index 2a5a49c724a8c555e05a3a15511d2235e17f7bef..b4c98f016d0e29fa61d91f501e12eddeb419c809 100644
--- a/db/migrate/20140914145549_migrate_to_new_members_model.rb
+++ b/db/migrate/20140914145549_migrate_to_new_members_model.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class MigrateToNewMembersModel < ActiveRecord::Migration
   def up
     execute "INSERT INTO members ( user_id, source_id, source_type, access_level, notification_level, type ) SELECT user_id, group_id, 'Namespace', group_access, notification_level, 'GroupMember' FROM users_groups"
diff --git a/db/migrate/20140914173417_remove_old_member_tables.rb b/db/migrate/20140914173417_remove_old_member_tables.rb
index 408b9551dbb466bdbf30284b741b8dfe2ed805c2..aff8e94e5be670a58467d81f2e77c6aca827363c 100644
--- a/db/migrate/20140914173417_remove_old_member_tables.rb
+++ b/db/migrate/20140914173417_remove_old_member_tables.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveOldMemberTables < ActiveRecord::Migration
   def up
     drop_table :users_groups
diff --git a/db/migrate/20141006143943_move_slack_service_to_webhook.rb b/db/migrate/20141006143943_move_slack_service_to_webhook.rb
index 5836cd6b8dba3fdfa9b7b0c782e915864c06e1ca..8cb120f7007b358f02ed253c9111d25112730481 100644
--- a/db/migrate/20141006143943_move_slack_service_to_webhook.rb
+++ b/db/migrate/20141006143943_move_slack_service_to_webhook.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class MoveSlackServiceToWebhook < ActiveRecord::Migration
   def change
     SlackService.all.each do |slack_service|
diff --git a/db/migrate/20141007100818_add_visibility_level_to_snippet.rb b/db/migrate/20141007100818_add_visibility_level_to_snippet.rb
index 93826185e8b1d8b4951c421932219a4175349169..688d8578478999893313460e94b102e9e67ba345 100644
--- a/db/migrate/20141007100818_add_visibility_level_to_snippet.rb
+++ b/db/migrate/20141007100818_add_visibility_level_to_snippet.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddVisibilityLevelToSnippet < ActiveRecord::Migration
   include Gitlab::Database
 
diff --git a/db/migrate/20141118150935_add_audit_event.rb b/db/migrate/20141118150935_add_audit_event.rb
index 07383c6bbc761b1bae2cab32edc627d590e85f3a..3884228456fc177f857e705940cc9b8bc464e109 100644
--- a/db/migrate/20141118150935_add_audit_event.rb
+++ b/db/migrate/20141118150935_add_audit_event.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddAuditEvent < ActiveRecord::Migration
   def change
     create_table :audit_events do |t|
diff --git a/db/migrate/20141121133009_add_timestamps_to_members.rb b/db/migrate/20141121133009_add_timestamps_to_members.rb
index ef6d4dedf32c95a69f85cc471def6bd66b392f36..68f164cd35d8acd5626b8ee2c2e19e7260e2a374 100644
--- a/db/migrate/20141121133009_add_timestamps_to_members.rb
+++ b/db/migrate/20141121133009_add_timestamps_to_members.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 # In 20140914145549_migrate_to_new_members_model.rb we forgot to set the
 # created_at and updated_at times for new records in the 'members' table. This
 # became a problem after commit c8e78d972a5a628870eefca0f2ccea0199c55bda which
diff --git a/db/migrate/20141121161704_add_identity_table.rb b/db/migrate/20141121161704_add_identity_table.rb
index a85b0426cec8b36e762a94767ff302876501296b..5a399f0d325229a9353b214d7c515221cb510167 100644
--- a/db/migrate/20141121161704_add_identity_table.rb
+++ b/db/migrate/20141121161704_add_identity_table.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddIdentityTable < ActiveRecord::Migration
   def up
     create_table :identities do |t|
diff --git a/db/migrate/20141205134006_add_locked_at_to_merge_request.rb b/db/migrate/20141205134006_add_locked_at_to_merge_request.rb
index 49651c44a82284c86289d2094d83a0421838e1e9..5aa91c7587afac05c8ce19ba59c49960907c0b57 100644
--- a/db/migrate/20141205134006_add_locked_at_to_merge_request.rb
+++ b/db/migrate/20141205134006_add_locked_at_to_merge_request.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddLockedAtToMergeRequest < ActiveRecord::Migration
   def change
     add_column :merge_requests, :locked_at, :datetime
diff --git a/db/migrate/20141216155758_create_doorkeeper_tables.rb b/db/migrate/20141216155758_create_doorkeeper_tables.rb
index af5aa7d8b734be6a2d240074aefb0d26c54e4203..b323ffe96f5085319982fa8a42d28836a732190b 100644
--- a/db/migrate/20141216155758_create_doorkeeper_tables.rb
+++ b/db/migrate/20141216155758_create_doorkeeper_tables.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateDoorkeeperTables < ActiveRecord::Migration
   def change
     create_table :oauth_applications do |t|
diff --git a/db/migrate/20141217125223_add_owner_to_application.rb b/db/migrate/20141217125223_add_owner_to_application.rb
index 7d5e6d07d0f21ae50a08ceb958b2f9ddbb1dc8c5..e5a669ab4d8b64be6e632a9d7b84b9563059d521 100644
--- a/db/migrate/20141217125223_add_owner_to_application.rb
+++ b/db/migrate/20141217125223_add_owner_to_application.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddOwnerToApplication < ActiveRecord::Migration
   def change
     add_column :oauth_applications, :owner_id, :integer, null: true
diff --git a/db/migrate/20141223135007_add_import_data_to_project_table.rb b/db/migrate/20141223135007_add_import_data_to_project_table.rb
index 5db78f94cc978c3a06511aa76ff6ccd9339bf58a..9c8a483e4d5fa15aa2a6a439939258c48a02443d 100644
--- a/db/migrate/20141223135007_add_import_data_to_project_table.rb
+++ b/db/migrate/20141223135007_add_import_data_to_project_table.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddImportDataToProjectTable < ActiveRecord::Migration
   def change
     add_column :projects, :import_type, :string
diff --git a/db/migrate/20141226080412_add_developers_can_push_to_protected_branches.rb b/db/migrate/20141226080412_add_developers_can_push_to_protected_branches.rb
index 70e7272f7f3f8640de1240bfdeb7f4d09133529f..a18b2f4974dd37719f64c130d0845a8ba8bd3dc9 100644
--- a/db/migrate/20141226080412_add_developers_can_push_to_protected_branches.rb
+++ b/db/migrate/20141226080412_add_developers_can_push_to_protected_branches.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddDevelopersCanPushToProtectedBranches < ActiveRecord::Migration
   def change
     add_column :protected_branches, :developers_can_push, :boolean, default: false, null: false
diff --git a/db/migrate/20150108073740_create_application_settings.rb b/db/migrate/20150108073740_create_application_settings.rb
index 651e35fdf7a9eca8021f8c5cf4c6dd442f7058c1..dfa2f765357b37a21df0f788128026688779b4e2 100644
--- a/db/migrate/20150108073740_create_application_settings.rb
+++ b/db/migrate/20150108073740_create_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateApplicationSettings < ActiveRecord::Migration
   def change
     create_table :application_settings do |t|
diff --git a/db/migrate/20150116234544_add_home_page_url_for_application_settings.rb b/db/migrate/20150116234544_add_home_page_url_for_application_settings.rb
index aa179ce3a4d29ec0ff236cb74a4c219afb47701e..10e6549c7298e1d0d789c0ab611f6087fd7dcc1d 100644
--- a/db/migrate/20150116234544_add_home_page_url_for_application_settings.rb
+++ b/db/migrate/20150116234544_add_home_page_url_for_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddHomePageUrlForApplicationSettings < ActiveRecord::Migration
   def change
     add_column :application_settings, :home_page_url, :string
diff --git a/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb b/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb
index c28ba3197ac80a5caa7931aa55540367ec3429d0..e083973615aeab002b29913b2be6f635b9a9e2b3 100644
--- a/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb
+++ b/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddGitlabAccessTokenToUser < ActiveRecord::Migration
   def change
     add_column :users, :gitlab_access_token, :string
diff --git a/db/migrate/20150125163100_add_default_branch_protection_setting.rb b/db/migrate/20150125163100_add_default_branch_protection_setting.rb
index 5020daf55f3df10ce2f8e79decd331ebdd168751..7ca3116d35440db2dc058952d4641394ccf3cae0 100644
--- a/db/migrate/20150125163100_add_default_branch_protection_setting.rb
+++ b/db/migrate/20150125163100_add_default_branch_protection_setting.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddDefaultBranchProtectionSetting < ActiveRecord::Migration
   def change
     add_column :application_settings, :default_branch_protection, :integer, :default => 2
diff --git a/db/migrate/20150205211843_add_timestamps_to_identities.rb b/db/migrate/20150205211843_add_timestamps_to_identities.rb
index 77cddbfec3b2b9af2c44df09031a193e13ee46ca..a78e28eb4ebcec5a6396b01676f1d10318560f5d 100644
--- a/db/migrate/20150205211843_add_timestamps_to_identities.rb
+++ b/db/migrate/20150205211843_add_timestamps_to_identities.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddTimestampsToIdentities < ActiveRecord::Migration
   def change
     add_timestamps(:identities)
diff --git a/db/migrate/20150206181414_add_index_to_created_at.rb b/db/migrate/20150206181414_add_index_to_created_at.rb
index fc624fca60dd8acc3c3960832cfccd84a53cf289..a161fad79dca64ab0a34754546bcb4c5aba7c2cf 100644
--- a/db/migrate/20150206181414_add_index_to_created_at.rb
+++ b/db/migrate/20150206181414_add_index_to_created_at.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddIndexToCreatedAt < ActiveRecord::Migration
   def change
     add_index "users", [:created_at, :id]
diff --git a/db/migrate/20150206222854_add_notification_email_to_user.rb b/db/migrate/20150206222854_add_notification_email_to_user.rb
index ab80f7e582f3022d75e3c0e6f734f44bc73293a2..ebae092cac8b784254c34f91fadcf72a6327894c 100644
--- a/db/migrate/20150206222854_add_notification_email_to_user.rb
+++ b/db/migrate/20150206222854_add_notification_email_to_user.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddNotificationEmailToUser < ActiveRecord::Migration
   def up
     add_column :users, :notification_email, :string
diff --git a/db/migrate/20150209222013_add_missing_index.rb b/db/migrate/20150209222013_add_missing_index.rb
index a816c2e9e8ca11e9754b371e809339b19138544d..18e3ac2cbbb76b3a937ac9b22cc170420d9ac563 100644
--- a/db/migrate/20150209222013_add_missing_index.rb
+++ b/db/migrate/20150209222013_add_missing_index.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddMissingIndex < ActiveRecord::Migration
   def change
     add_index "services", [:created_at, :id]
diff --git a/db/migrate/20150211172122_add_template_to_service.rb b/db/migrate/20150211172122_add_template_to_service.rb
index b1bfbc45ee9ab36c5f48a69d16e868e6f3c7ca56..a3e96b25c567d450ff50a5b113d532f6dc1b527e 100644
--- a/db/migrate/20150211172122_add_template_to_service.rb
+++ b/db/migrate/20150211172122_add_template_to_service.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddTemplateToService < ActiveRecord::Migration
   def change
     add_column :services, :template, :boolean, default: false
diff --git a/db/migrate/20150211174341_allow_null_in_services_project_id.rb b/db/migrate/20150211174341_allow_null_in_services_project_id.rb
index 68f0281279159537ca2cbb2990e967fd3d3d7277..fea95c79adf65516b801485b3b6b778d4e1ebe5e 100644
--- a/db/migrate/20150211174341_allow_null_in_services_project_id.rb
+++ b/db/migrate/20150211174341_allow_null_in_services_project_id.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AllowNullInServicesProjectId < ActiveRecord::Migration
   def change
     change_column :services, :project_id, :integer, null: true
diff --git a/db/migrate/20150213104043_add_twitter_sharing_enabled_to_application_settings.rb b/db/migrate/20150213104043_add_twitter_sharing_enabled_to_application_settings.rb
index a0439172391f07f26e4f08ee1e4ff027ad40d571..334020376e40c0afb3a7a08c9f85664373137800 100644
--- a/db/migrate/20150213104043_add_twitter_sharing_enabled_to_application_settings.rb
+++ b/db/migrate/20150213104043_add_twitter_sharing_enabled_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddTwitterSharingEnabledToApplicationSettings < ActiveRecord::Migration
   def change
     add_column :application_settings, :twitter_sharing_enabled, :boolean, default: true
diff --git a/db/migrate/20150213114800_add_hide_no_password_to_user.rb b/db/migrate/20150213114800_add_hide_no_password_to_user.rb
index 685f08442762183f53bad4ec097324aec9b04971..a2af3510b9c1c98b5f3ce022966fb34199e81704 100644
--- a/db/migrate/20150213114800_add_hide_no_password_to_user.rb
+++ b/db/migrate/20150213114800_add_hide_no_password_to_user.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddHideNoPasswordToUser < ActiveRecord::Migration
   def change
     add_column :users, :hide_no_password, :boolean, default: false
diff --git a/db/migrate/20150213121042_add_password_automatically_set_to_user.rb b/db/migrate/20150213121042_add_password_automatically_set_to_user.rb
index c3c7c1ffc77d8f10e4c1e6fd3b2f2018c1c31d8d..4e84a13f0d2aca282078b12baffc218c758031d4 100644
--- a/db/migrate/20150213121042_add_password_automatically_set_to_user.rb
+++ b/db/migrate/20150213121042_add_password_automatically_set_to_user.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddPasswordAutomaticallySetToUser < ActiveRecord::Migration
   def change
     add_column :users, :password_automatically_set, :boolean, default: false
diff --git a/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb b/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb
index 23ac1b399ec4007dc91ff6ab893015979ecbb1cb..78e9fd0c3a92f802dcbfda7dfaf69a5e43fb2ce0 100644
--- a/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb
+++ b/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddBitbucketAccessTokenAndSecretToUser < ActiveRecord::Migration
   def change
     add_column :users, :bitbucket_access_token, :string
diff --git a/db/migrate/20150219004514_add_events_to_services.rb b/db/migrate/20150219004514_add_events_to_services.rb
index cf73a0174f4f6d7992729bc79b08f7952285aedb..560382c3fa1e026a56618319cd47436597772127 100644
--- a/db/migrate/20150219004514_add_events_to_services.rb
+++ b/db/migrate/20150219004514_add_events_to_services.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddEventsToServices < ActiveRecord::Migration
   def change
     add_column :services, :push_events, :boolean, :default => true
diff --git a/db/migrate/20150223022001_set_missing_last_activity_at.rb b/db/migrate/20150223022001_set_missing_last_activity_at.rb
index 3f6d4d83474f639fa4fdfa102576051b020c76f4..300381ad65bb3ccd5a3630bdf29b2edb5c106cec 100644
--- a/db/migrate/20150223022001_set_missing_last_activity_at.rb
+++ b/db/migrate/20150223022001_set_missing_last_activity_at.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class SetMissingLastActivityAt < ActiveRecord::Migration
   def up
     execute "UPDATE projects SET last_activity_at = updated_at WHERE last_activity_at IS NULL"
diff --git a/db/migrate/20150225065047_add_note_events_to_services.rb b/db/migrate/20150225065047_add_note_events_to_services.rb
index d54ba9e482f846fb8170227a0fd80197e9438b0f..7843cabc43b477f52e8270938cc66cd2c2c5b26c 100644
--- a/db/migrate/20150225065047_add_note_events_to_services.rb
+++ b/db/migrate/20150225065047_add_note_events_to_services.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddNoteEventsToServices < ActiveRecord::Migration
   def change
     add_column :services, :note_events, :boolean, default: true, null: false
diff --git a/db/migrate/20150301014758_add_restricted_visibility_levels_to_application_settings.rb b/db/migrate/20150301014758_add_restricted_visibility_levels_to_application_settings.rb
index 494c3033bfff79c57151b773fe72abb2a8195860..7d8d65ef2eecec91c8f913a2f2bb38fa690aac9d 100644
--- a/db/migrate/20150301014758_add_restricted_visibility_levels_to_application_settings.rb
+++ b/db/migrate/20150301014758_add_restricted_visibility_levels_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddRestrictedVisibilityLevelsToApplicationSettings < ActiveRecord::Migration
   def change
     add_column :application_settings, :restricted_visibility_levels, :text
diff --git a/db/migrate/20150306023106_fix_namespace_duplication.rb b/db/migrate/20150306023106_fix_namespace_duplication.rb
index 334e5574559e361a717b5d6ad6951ce0efd554d2..ea53a9d71f23e32677e48888b4962e8a0a3595d7 100644
--- a/db/migrate/20150306023106_fix_namespace_duplication.rb
+++ b/db/migrate/20150306023106_fix_namespace_duplication.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class FixNamespaceDuplication < ActiveRecord::Migration
   def up
     #fixes path duplication
diff --git a/db/migrate/20150306023112_add_unique_index_to_namespace.rb b/db/migrate/20150306023112_add_unique_index_to_namespace.rb
index 6472138e3eff92e0b9fda45f40644bab4acc441b..f293a9b643fbe212531c3227ff7f892a6e25731d 100644
--- a/db/migrate/20150306023112_add_unique_index_to_namespace.rb
+++ b/db/migrate/20150306023112_add_unique_index_to_namespace.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddUniqueIndexToNamespace < ActiveRecord::Migration
   def change
     remove_index :namespaces, column: :name if index_exists?(:namespaces, :name)
diff --git a/db/migrate/20150310194358_add_version_check_to_application_settings.rb b/db/migrate/20150310194358_add_version_check_to_application_settings.rb
index e9d42c1e749732a763ff9d69f40cbc73d05327ca..5d3dae6e7d80b9cb0d7ac9517d169b8b4d272eb1 100644
--- a/db/migrate/20150310194358_add_version_check_to_application_settings.rb
+++ b/db/migrate/20150310194358_add_version_check_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddVersionCheckToApplicationSettings < ActiveRecord::Migration
   def change
     add_column :application_settings, :version_check_enabled, :boolean, default: true
diff --git a/db/migrate/20150312000132_add_group_id_to_web_hooks.rb b/db/migrate/20150312000132_add_group_id_to_web_hooks.rb
index 074a71c4f217a194f031b622279ffa7858fe984a..460f110c1b0cb6b025bd3a2be34e6b659136b454 100644
--- a/db/migrate/20150312000132_add_group_id_to_web_hooks.rb
+++ b/db/migrate/20150312000132_add_group_id_to_web_hooks.rb
@@ -2,4 +2,4 @@ class AddGroupIdToWebHooks < ActiveRecord::Migration
   def change
     add_column :web_hooks, :group_id, :integer, after: :project_id
   end
-end
\ No newline at end of file
+end
diff --git a/db/migrate/20150313012111_create_subscriptions_table.rb b/db/migrate/20150313012111_create_subscriptions_table.rb
index a1d4d9dedc57048388dfe1a3109c7fc6c09ace7e..8adb193b27fc40f837f7a781aa0b7f7827d64c63 100644
--- a/db/migrate/20150313012111_create_subscriptions_table.rb
+++ b/db/migrate/20150313012111_create_subscriptions_table.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateSubscriptionsTable < ActiveRecord::Migration
   def change
     create_table :subscriptions do |t|
diff --git a/db/migrate/20150320234437_add_location_to_user.rb b/db/migrate/20150320234437_add_location_to_user.rb
index 32731d37d7558c4a01a3577372ea9ea8463f11d2..df04657036167e68fe4e1d36ae9813b681630011 100644
--- a/db/migrate/20150320234437_add_location_to_user.rb
+++ b/db/migrate/20150320234437_add_location_to_user.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddLocationToUser < ActiveRecord::Migration
   def change
     add_column :users, :location, :string
diff --git a/db/migrate/20150324155957_set_incorrect_assignee_id_to_null.rb b/db/migrate/20150324155957_set_incorrect_assignee_id_to_null.rb
index 42dc8173e46ee315b46d68c8a2ed3a040427956e..9f8b6f4bd59af0ce8c01075a1235021733dce8d2 100644
--- a/db/migrate/20150324155957_set_incorrect_assignee_id_to_null.rb
+++ b/db/migrate/20150324155957_set_incorrect_assignee_id_to_null.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class SetIncorrectAssigneeIdToNull < ActiveRecord::Migration
   def up
     execute "UPDATE issues SET assignee_id = NULL WHERE assignee_id = -1"
diff --git a/db/migrate/20150327122227_add_public_to_key.rb b/db/migrate/20150327122227_add_public_to_key.rb
index 6ffbf4cda193e41c239c0cbeb7d995f624ffc1b7..33c20d65e034314cb2216ec4f130b15f2bb2d3db 100644
--- a/db/migrate/20150327122227_add_public_to_key.rb
+++ b/db/migrate/20150327122227_add_public_to_key.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddPublicToKey < ActiveRecord::Migration
   def change
     add_column :keys, :public, :boolean, default: false, null: false
diff --git a/db/migrate/20150327150017_add_import_data_to_project.rb b/db/migrate/20150327150017_add_import_data_to_project.rb
index 12c00339eec41f067b0e254788c0f7045c8a79c2..67b1554dfd11fc4113834801ad5fd63fad91a8fb 100644
--- a/db/migrate/20150327150017_add_import_data_to_project.rb
+++ b/db/migrate/20150327150017_add_import_data_to_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddImportDataToProject < ActiveRecord::Migration
   def change
     add_column :projects, :import_data, :text
diff --git a/db/migrate/20150327223628_add_devise_two_factor_to_users.rb b/db/migrate/20150327223628_add_devise_two_factor_to_users.rb
index 11b026ee8f3470d790b93c8190c84ab6c7f28e81..eccb0123e779cd6172440af2a03bc5f28e75ccb6 100644
--- a/db/migrate/20150327223628_add_devise_two_factor_to_users.rb
+++ b/db/migrate/20150327223628_add_devise_two_factor_to_users.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddDeviseTwoFactorToUsers < ActiveRecord::Migration
   def change
     add_column :users, :encrypted_otp_secret, :string
diff --git a/db/migrate/20150328132231_add_max_attachment_size_to_application_settings.rb b/db/migrate/20150328132231_add_max_attachment_size_to_application_settings.rb
index 1d161674a9a90a5a285e0545dd557d756a3872e8..4c56a2fb78bc937bb1f3c3450e9198ad68bccc42 100644
--- a/db/migrate/20150328132231_add_max_attachment_size_to_application_settings.rb
+++ b/db/migrate/20150328132231_add_max_attachment_size_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddMaxAttachmentSizeToApplicationSettings < ActiveRecord::Migration
   def change
     add_column :application_settings, :max_attachment_size, :integer, default: 10, null: false
diff --git a/db/migrate/20150331183602_add_devise_two_factor_backupable_to_users.rb b/db/migrate/20150331183602_add_devise_two_factor_backupable_to_users.rb
index 913958db7c589b8c363fa019644ec6a29c493f8a..fdb6d72917e8f6a460894498b79af8961d3d584b 100644
--- a/db/migrate/20150331183602_add_devise_two_factor_backupable_to_users.rb
+++ b/db/migrate/20150331183602_add_devise_two_factor_backupable_to_users.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddDeviseTwoFactorBackupableToUsers < ActiveRecord::Migration
   def change
     add_column :users, :otp_backup_codes, :text
diff --git a/db/migrate/20150406133311_add_invite_data_to_member.rb b/db/migrate/20150406133311_add_invite_data_to_member.rb
index 5d3e856ddcefde45c67ad7d9d6eb18277d1248bb..63d0f184f32dde1d4eb2f681277647a37c73039b 100644
--- a/db/migrate/20150406133311_add_invite_data_to_member.rb
+++ b/db/migrate/20150406133311_add_invite_data_to_member.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddInviteDataToMember < ActiveRecord::Migration
   def up
     add_column :members, :created_by_id, :integer
diff --git a/db/migrate/20150411000035_fix_identities.rb b/db/migrate/20150411000035_fix_identities.rb
index d9051f9fffdc0004da8de01fc026b13a2e25529b..a10fcc001f4f893e59fe96a61d7abcb5f494f9dd 100644
--- a/db/migrate/20150411000035_fix_identities.rb
+++ b/db/migrate/20150411000035_fix_identities.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class FixIdentities < ActiveRecord::Migration
   def up
     # Up until now, legacy 'ldap' references in the database were charitably
diff --git a/db/migrate/20150411180045_rename_buildbox_service.rb b/db/migrate/20150411180045_rename_buildbox_service.rb
index 5a0b5d07e50f91ee6c23ae445affafd56c7232ef..9f3b25c39713273c04d1a4e8036e05617000cdc8 100644
--- a/db/migrate/20150411180045_rename_buildbox_service.rb
+++ b/db/migrate/20150411180045_rename_buildbox_service.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RenameBuildboxService < ActiveRecord::Migration
   def up
     execute "UPDATE services SET type = 'BuildkiteService' WHERE type = 'BuildboxService';"
diff --git a/db/migrate/20150413192223_add_public_email_to_users.rb b/db/migrate/20150413192223_add_public_email_to_users.rb
index 700e9f343a68f418c88aeab75c90535e3677949f..0fed5eaf4616ab6b4187d47cbca17ba1800e4e25 100644
--- a/db/migrate/20150413192223_add_public_email_to_users.rb
+++ b/db/migrate/20150413192223_add_public_email_to_users.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddPublicEmailToUsers < ActiveRecord::Migration
   def change
     add_column :users, :public_email, :string, default: "", null: false
diff --git a/db/migrate/20150417121913_create_project_import_data.rb b/db/migrate/20150417121913_create_project_import_data.rb
index c78f5fde85e0598ff39cc508f2c0d4af5020501b..fc357cbacc8d62a38be67e666468e2011cc43f08 100644
--- a/db/migrate/20150417121913_create_project_import_data.rb
+++ b/db/migrate/20150417121913_create_project_import_data.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateProjectImportData < ActiveRecord::Migration
   def change
     create_table :project_import_data do |t|
diff --git a/db/migrate/20150417122318_remove_import_data_from_project.rb b/db/migrate/20150417122318_remove_import_data_from_project.rb
index 46cf63593c90b5b6cec19a8ebbd8925fa5c70b8c..5a008218fa5f34f24f721c1356a56c8a112135cf 100644
--- a/db/migrate/20150417122318_remove_import_data_from_project.rb
+++ b/db/migrate/20150417122318_remove_import_data_from_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveImportDataFromProject < ActiveRecord::Migration
   def up
     remove_column :projects, :import_data
diff --git a/db/migrate/20150421120000_remove_periods_at_ends_of_usernames.rb b/db/migrate/20150421120000_remove_periods_at_ends_of_usernames.rb
index 3057ea3c68c22f89141948d475662b2df4a90ca8..3445e9ce59e20e7cbdfab36845ee0e5cb4713fc2 100644
--- a/db/migrate/20150421120000_remove_periods_at_ends_of_usernames.rb
+++ b/db/migrate/20150421120000_remove_periods_at_ends_of_usernames.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemovePeriodsAtEndsOfUsernames < ActiveRecord::Migration
   include Gitlab::ShellAdapter
 
diff --git a/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb b/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb
index 50a9b2439e0934612fb80946c6ed9b6feed02abc..129ce4d04afe3fb2fe3e2b575583c4830be5d592 100644
--- a/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb
+++ b/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddDefaultProjectVisibililtyToApplicationSettings < ActiveRecord::Migration
   def up
     add_column :application_settings, :default_project_visibility, :integer
diff --git a/db/migrate/20150425164646_gitlab_change_collation_for_tag_names.acts_as_taggable_on_engine.rb b/db/migrate/20150425164646_gitlab_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
index 281c88d2a7d401c21a5455d20e4b24742c1cda4b..8f352414ffded8b37ae6f52547ca9091d1c56dcb 100644
--- a/db/migrate/20150425164646_gitlab_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
+++ b/db/migrate/20150425164646_gitlab_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 # This migration is a duplicate of 20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
 # It shold be applied before the index additions to ensure that `name` is case sensitive.
 
diff --git a/db/migrate/20150425164647_remove_duplicate_tags.rb b/db/migrate/20150425164647_remove_duplicate_tags.rb
index 13e5038db9c4c896611e8fed6c1e7ef1f0267329..e77623bf5078f070bbc5571cbdcc51f3e14a996a 100644
--- a/db/migrate/20150425164647_remove_duplicate_tags.rb
+++ b/db/migrate/20150425164647_remove_duplicate_tags.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveDuplicateTags < ActiveRecord::Migration
   def up
     select_all("SELECT name, COUNT(id) as cnt FROM tags GROUP BY name HAVING COUNT(id) > 1").each do |tag|
diff --git a/db/migrate/20150425164648_add_missing_unique_indices.acts_as_taggable_on_engine.rb b/db/migrate/20150425164648_add_missing_unique_indices.acts_as_taggable_on_engine.rb
index c1b786815197de830889229f8dba71352b5fffdf..cbff98cdbc449c80a524a480fa7da4bc8d9b504d 100644
--- a/db/migrate/20150425164648_add_missing_unique_indices.acts_as_taggable_on_engine.rb
+++ b/db/migrate/20150425164648_add_missing_unique_indices.acts_as_taggable_on_engine.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 # This migration comes from acts_as_taggable_on_engine (originally 2)
 class AddMissingUniqueIndices < ActiveRecord::Migration
   def self.up
diff --git a/db/migrate/20150425164649_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb b/db/migrate/20150425164649_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb
index 8edb508078131059ac506257527d5dc02bad67be..1568d2dd4ceaeaf45bb3e5693a0222da8fa6a1cc 100644
--- a/db/migrate/20150425164649_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb
+++ b/db/migrate/20150425164649_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 # This migration comes from acts_as_taggable_on_engine (originally 3)
 class AddTaggingsCounterCacheToTags < ActiveRecord::Migration
   def self.up
diff --git a/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb b/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb
index 71f2d7f43309d0628ec0120e211c945559c46846..88829b877115d612ce8ed48ccc7f0a604a3c802c 100644
--- a/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb
+++ b/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 # This migration comes from acts_as_taggable_on_engine (originally 4)
 class AddMissingTaggableIndex < ActiveRecord::Migration
   def self.up
diff --git a/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb b/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
index bfb06bc7cda48c12580961fc53373a6153ac7f68..642c47453210eafa2d8837c404949879be205960 100644
--- a/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
+++ b/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 # This migration comes from acts_as_taggable_on_engine (originally 5)
 # This migration is added to circumvent issue #623 and have special characters
 # work properly
diff --git a/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb b/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb
index 8f1b0cc8935aa7f19727b035495dca2f8d5d84c4..dd13def4176f5653e97006eb5ce42eacf4f295ae 100644
--- a/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb
+++ b/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddDefaultSnippetVisibilityToAppSettings < ActiveRecord::Migration
   def up
     add_column :application_settings, :default_snippet_visibility, :integer
diff --git a/db/migrate/20150429002313_remove_abandoned_group_members_records.rb b/db/migrate/20150429002313_remove_abandoned_group_members_records.rb
index 244637e1c4a1263255db97f3c77acba3efab0fa2..d2c7f3c442ed961f2e3c084f3818b71eb058b10f 100644
--- a/db/migrate/20150429002313_remove_abandoned_group_members_records.rb
+++ b/db/migrate/20150429002313_remove_abandoned_group_members_records.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveAbandonedGroupMembersRecords < ActiveRecord::Migration
   def up
     execute("DELETE FROM members WHERE type = 'GroupMember' AND source_id NOT IN(\
diff --git a/db/migrate/20150502064022_add_restricted_signup_domains_to_application_settings.rb b/db/migrate/20150502064022_add_restricted_signup_domains_to_application_settings.rb
index 184e26536109e3b380959e59efd311a3c5388a53..b63ea9aec7a8752970040388c702371e7e392e5d 100644
--- a/db/migrate/20150502064022_add_restricted_signup_domains_to_application_settings.rb
+++ b/db/migrate/20150502064022_add_restricted_signup_domains_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddRestrictedSignupDomainsToApplicationSettings < ActiveRecord::Migration
   def change
     add_column :application_settings, :restricted_signup_domains, :text
diff --git a/db/migrate/20150509180749_convert_legacy_reference_notes.rb b/db/migrate/20150509180749_convert_legacy_reference_notes.rb
index b02605489be33f3c7607b7dd3f3f8e1b721aefe7..cd8bf90108da198fd737a5906733a2d739e76076 100644
--- a/db/migrate/20150509180749_convert_legacy_reference_notes.rb
+++ b/db/migrate/20150509180749_convert_legacy_reference_notes.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 # Convert legacy Markdown-emphasized notes to the current, non-emphasized format
 #
 #   _mentioned in 54f7727c850972f0401c1312a7c4a6a380de5666_
diff --git a/db/migrate/20150516060434_add_note_events_to_web_hooks.rb b/db/migrate/20150516060434_add_note_events_to_web_hooks.rb
index 0097587b4f6dc0c057b668235d4bcd5a392bcdef..bf72e5e2e3adc3c5d5f6f4649569001a02b8c07d 100644
--- a/db/migrate/20150516060434_add_note_events_to_web_hooks.rb
+++ b/db/migrate/20150516060434_add_note_events_to_web_hooks.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddNoteEventsToWebHooks < ActiveRecord::Migration
   def up
     add_column :web_hooks, :note_events, :boolean, default: false, null: false
diff --git a/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb b/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb
index 6a78294f0b2985d668dda2233033f4e934bcc37e..9b02eda56abfc4e76a7f7c7758fe99cca822369a 100644
--- a/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb
+++ b/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddUserOauthApplicationsToApplicationSettings < ActiveRecord::Migration
   def change
     add_column :application_settings, :user_oauth_applications, :bool, default: true
diff --git a/db/migrate/20150529150354_add_after_sign_out_path_for_application_settings.rb b/db/migrate/20150529150354_add_after_sign_out_path_for_application_settings.rb
index 83e081014075f26cf308e49be91fe40c80f76071..833c36de52d5e0526aa80890397669519ed3ff58 100644
--- a/db/migrate/20150529150354_add_after_sign_out_path_for_application_settings.rb
+++ b/db/migrate/20150529150354_add_after_sign_out_path_for_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddAfterSignOutPathForApplicationSettings < ActiveRecord::Migration
   def change
     add_column :application_settings, :after_sign_out_path, :string
diff --git a/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb b/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb
index 61ff0af41f4c9dce2143c666833a3e641399ac07..1f5cf1fe5f1c01c7771d25f9031a30e28b9161f9 100644
--- a/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb
+++ b/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddSessionExpireDelayForApplicationSettings < ActiveRecord::Migration
   def change
     unless column_exists?(:application_settings, :session_expire_delay)
diff --git a/db/migrate/20150610065936_add_dashboard_to_users.rb b/db/migrate/20150610065936_add_dashboard_to_users.rb
index 2628e450722f21f1c699690d555e9246f37c8350..df38472f89332236cffe6447e30e3ad2842741af 100644
--- a/db/migrate/20150610065936_add_dashboard_to_users.rb
+++ b/db/migrate/20150610065936_add_dashboard_to_users.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddDashboardToUsers < ActiveRecord::Migration
   def up
     add_column :users, :dashboard, :integer, default: 0
diff --git a/db/migrate/20150620233230_add_default_otp_required_for_login_value.rb b/db/migrate/20150620233230_add_default_otp_required_for_login_value.rb
index 8eed8678b2f0a5b9f92639858d91334e98b36659..da0fd457a34d2c7e05f389290ca0d2320eb22274 100644
--- a/db/migrate/20150620233230_add_default_otp_required_for_login_value.rb
+++ b/db/migrate/20150620233230_add_default_otp_required_for_login_value.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddDefaultOtpRequiredForLoginValue < ActiveRecord::Migration
   def up
     execute %q{UPDATE users SET otp_required_for_login = FALSE WHERE otp_required_for_login IS NULL}
diff --git a/db/migrate/20150713160110_add_project_view_to_users.rb b/db/migrate/20150713160110_add_project_view_to_users.rb
index fe3d206df891eedd6eee58aa63add2b5c30bfda8..0de5a93035c369a27027b74bac19ec29893dd2d9 100644
--- a/db/migrate/20150713160110_add_project_view_to_users.rb
+++ b/db/migrate/20150713160110_add_project_view_to_users.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddProjectViewToUsers < ActiveRecord::Migration
   def change
     add_column :users, :project_view, :integer, default: 0
diff --git a/db/migrate/20150717130904_add_commits_count_to_project.rb b/db/migrate/20150717130904_add_commits_count_to_project.rb
index 9b46daa5933901797740130f80a672c965f21463..5799e068c693c0f5aee55685d621be83b221f1f0 100644
--- a/db/migrate/20150717130904_add_commits_count_to_project.rb
+++ b/db/migrate/20150717130904_add_commits_count_to_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddCommitsCountToProject < ActiveRecord::Migration
   def change
     add_column :projects, :commit_count, :integer, default: 0
diff --git a/db/migrate/20150730122406_add_updated_by_to_issuables_and_notes.rb b/db/migrate/20150730122406_add_updated_by_to_issuables_and_notes.rb
index 78d45c7f96b8dbad7e5f36b1ea0b7ceca7499096..be30e881c746f346372fca323693471115dc9c96 100644
--- a/db/migrate/20150730122406_add_updated_by_to_issuables_and_notes.rb
+++ b/db/migrate/20150730122406_add_updated_by_to_issuables_and_notes.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddUpdatedByToIssuablesAndNotes < ActiveRecord::Migration
   def change
     add_column :notes, :updated_by_id, :integer
diff --git a/db/migrate/20150806104937_create_abuse_reports.rb b/db/migrate/20150806104937_create_abuse_reports.rb
index e97dc4cf04cf03cfbb01baaf946ac38b11a8d0d6..3c749b5d9a9b969dd768f946f5705fafc7bb7d5d 100644
--- a/db/migrate/20150806104937_create_abuse_reports.rb
+++ b/db/migrate/20150806104937_create_abuse_reports.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateAbuseReports < ActiveRecord::Migration
   def change
     create_table :abuse_reports do |t|
diff --git a/db/migrate/20150812080800_add_settings_import_sources.rb b/db/migrate/20150812080800_add_settings_import_sources.rb
index 276d2fdb2b13f714629eb1566aa416d173bc5547..07f417fa3e3100a3e73cd06fea4f8135482de8f1 100644
--- a/db/migrate/20150812080800_add_settings_import_sources.rb
+++ b/db/migrate/20150812080800_add_settings_import_sources.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 require 'yaml'
 
 class AddSettingsImportSources < ActiveRecord::Migration
diff --git a/db/migrate/20150814065925_remove_oauth_tokens_from_users.rb b/db/migrate/20150814065925_remove_oauth_tokens_from_users.rb
index de2078a9268578b8569be64125eeb40ad538441a..7eaa7eda311308a117ba8f10ec91977af2dbf3bf 100644
--- a/db/migrate/20150814065925_remove_oauth_tokens_from_users.rb
+++ b/db/migrate/20150814065925_remove_oauth_tokens_from_users.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveOauthTokensFromUsers < ActiveRecord::Migration
   def change
     remove_column :users, :github_access_token, :string
diff --git a/db/migrate/20150817163600_deduplicate_user_identities.rb b/db/migrate/20150817163600_deduplicate_user_identities.rb
index fceffc48018637bd7cf59d30c7ae01090e7a1441..b0cfad7d20f299ed86b7beffbb81b3dffb28b96b 100644
--- a/db/migrate/20150817163600_deduplicate_user_identities.rb
+++ b/db/migrate/20150817163600_deduplicate_user_identities.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class DeduplicateUserIdentities < ActiveRecord::Migration
   def change
     execute 'DROP TABLE IF EXISTS tt_migration_DeduplicateUserIdentities;'
diff --git a/db/migrate/20150818213832_add_sent_notifications.rb b/db/migrate/20150818213832_add_sent_notifications.rb
index 43e8d6a1a82e2d620e41ba6b14326424e383ec0f..fa0c3ce0acfda7d9f675050369796bbf2cfad876 100644
--- a/db/migrate/20150818213832_add_sent_notifications.rb
+++ b/db/migrate/20150818213832_add_sent_notifications.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddSentNotifications < ActiveRecord::Migration
   def change
     create_table :sent_notifications do |t|
diff --git a/db/migrate/20150824002011_add_enable_ssl_verification.rb b/db/migrate/20150824002011_add_enable_ssl_verification.rb
index 093c068fbde7ecee87d1e7cc978cacfebfc37794..6e992f0883429ff406bd08018f1dc314e4db8c86 100644
--- a/db/migrate/20150824002011_add_enable_ssl_verification.rb
+++ b/db/migrate/20150824002011_add_enable_ssl_verification.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddEnableSslVerification < ActiveRecord::Migration
   def change
     add_column :web_hooks, :enable_ssl_verification, :boolean, default: false
diff --git a/db/migrate/20150826001931_add_ci_tables.rb b/db/migrate/20150826001931_add_ci_tables.rb
index c4f51363e57dec2838370d6649fdc80fa8e42e56..d1f8506d1fe182f9213514c63e8e0c6ea43dee2d 100644
--- a/db/migrate/20150826001931_add_ci_tables.rb
+++ b/db/migrate/20150826001931_add_ci_tables.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddCiTables < ActiveRecord::Migration
   def change
     create_table "ci_application_settings", force: true do |t|
diff --git a/db/migrate/20150902001023_add_template_to_label.rb b/db/migrate/20150902001023_add_template_to_label.rb
index bd381a97b6939bf1a1916cfef767b563fb976323..0f6ae8d6cc3ea23d076a32de2874910ba0411d98 100644
--- a/db/migrate/20150902001023_add_template_to_label.rb
+++ b/db/migrate/20150902001023_add_template_to_label.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddTemplateToLabel < ActiveRecord::Migration
   def change
     add_column :labels, :template, :boolean, default: false
diff --git a/db/migrate/20150914215247_add_ci_tags.rb b/db/migrate/20150914215247_add_ci_tags.rb
index df3390e8a82b1e353bfd9d9e632b8d823802d036..b647bc9c8a2f87a0db0e4c15ddcc08131dd4beab 100644
--- a/db/migrate/20150914215247_add_ci_tags.rb
+++ b/db/migrate/20150914215247_add_ci_tags.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddCiTags < ActiveRecord::Migration
   def change
     create_table "ci_taggings", force: true do |t|
diff --git a/db/migrate/20150915001905_enable_ssl_verification_by_default.rb b/db/migrate/20150915001905_enable_ssl_verification_by_default.rb
index 6e924262a13eaa65cc65626b6cd0f26522e33121..3f070139418b00d7ec54f4084972f6e62f170d5b 100644
--- a/db/migrate/20150915001905_enable_ssl_verification_by_default.rb
+++ b/db/migrate/20150915001905_enable_ssl_verification_by_default.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class EnableSslVerificationByDefault < ActiveRecord::Migration
   def change
     change_column :web_hooks, :enable_ssl_verification, :boolean, default: true
diff --git a/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb b/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb
index 90ce6c2db3da1ea6ccf7f48bbad547e813f77538..ea2ab6e40936cb720499bb410812623ae40aef2b 100644
--- a/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb
+++ b/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class EnableSslVerificationForWebHooks < ActiveRecord::Migration
   def up
     execute("UPDATE web_hooks SET enable_ssl_verification = true")
diff --git a/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb b/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb
index 37a27f11935c39640fb8a8ffffddff9831a05894..a504f25b1be62786349e64e9f1c65ab3ecfcaa0a 100644
--- a/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb
+++ b/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddHelpPageTextToApplicationSettings < ActiveRecord::Migration
   def change
     add_column :application_settings, :help_page_text, :text
diff --git a/db/migrate/20150916145038_add_index_for_committed_at_and_id.rb b/db/migrate/20150916145038_add_index_for_committed_at_and_id.rb
index 78d9e5f61a1b562a307182059cf4c971d598f142..a18ed93cf37b032c9b62086fd6fc9c68973ab6e6 100644
--- a/db/migrate/20150916145038_add_index_for_committed_at_and_id.rb
+++ b/db/migrate/20150916145038_add_index_for_committed_at_and_id.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddIndexForCommittedAtAndId < ActiveRecord::Migration
   def change
     add_index :ci_commits, [:project_id, :committed_at, :id]
diff --git a/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb b/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb
index 6cf668a170e763748efa225a59d87818d5494789..c9b6e035122ab471421a5d566a9232edba22a19d 100644
--- a/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb
+++ b/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddCiEnabledToApplicationSettings < ActiveRecord::Migration
   def change
     add_column :application_settings, :ci_enabled, :boolean, null: false, default: true
diff --git a/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb b/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb
index 0aad6fe5e6e3a9176222c3f7b360d1e8c7ef1ba8..e1818b566d798f56a00daeefa636a0ccde2ff4d2 100644
--- a/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb
+++ b/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveInvalidMilestonesFromMergeRequests < ActiveRecord::Migration
   def up
     execute("UPDATE merge_requests SET milestone_id = NULL where milestone_id NOT IN (SELECT id FROM milestones)")
diff --git a/db/migrate/20150920010715_add_consumed_timestep_to_users.rb b/db/migrate/20150920010715_add_consumed_timestep_to_users.rb
index c8438b3f6aabd9caa9a4921b303323552f64554b..e6975f5b9fec9a28a031da29a236d007ded2590c 100644
--- a/db/migrate/20150920010715_add_consumed_timestep_to_users.rb
+++ b/db/migrate/20150920010715_add_consumed_timestep_to_users.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddConsumedTimestepToUsers < ActiveRecord::Migration
   def change
     add_column :users, :consumed_timestep, :integer
diff --git a/db/migrate/20150920161119_add_line_code_to_sent_notification.rb b/db/migrate/20150920161119_add_line_code_to_sent_notification.rb
index d9af4e71751f886e94336f84d9b325fbeb020a15..1bcb06e4bda3367fa18c888b3e8b466e0f775278 100644
--- a/db/migrate/20150920161119_add_line_code_to_sent_notification.rb
+++ b/db/migrate/20150920161119_add_line_code_to_sent_notification.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddLineCodeToSentNotification < ActiveRecord::Migration
   def change
     add_column :sent_notifications, :line_code, :string
diff --git a/db/migrate/20150924125150_add_project_id_to_ci_commit.rb b/db/migrate/20150924125150_add_project_id_to_ci_commit.rb
index 1a761fe0f863f057b676bef372fb33cd094020a1..905332b7dc79a6fb2e758a20f51ec7c8788199da 100644
--- a/db/migrate/20150924125150_add_project_id_to_ci_commit.rb
+++ b/db/migrate/20150924125150_add_project_id_to_ci_commit.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddProjectIdToCiCommit < ActiveRecord::Migration
   def up
     add_column :ci_commits, :gl_project_id, :integer
diff --git a/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb b/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb
index 2be57b6062ebac7e17848c8ee3e8ca9485360b4b..fb0e0ba1fa54d8341258a3ab22ac71981ef57fa2 100644
--- a/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb
+++ b/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class MigrateProjectIdForCiCommits < ActiveRecord::Migration
   def up
     subquery = 'SELECT gitlab_id FROM ci_projects WHERE ci_projects.id = ci_commits.project_id'
diff --git a/db/migrate/20150930001110_merge_request_error_field.rb b/db/migrate/20150930001110_merge_request_error_field.rb
index c2ee498ef3ffe8d64e1b117273c70c1b5fea0102..71a8ae3938a0cbe7e5fc461eab86dca17a0d6bff 100644
--- a/db/migrate/20150930001110_merge_request_error_field.rb
+++ b/db/migrate/20150930001110_merge_request_error_field.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class MergeRequestErrorField < ActiveRecord::Migration
   def up
     add_column :merge_requests, :merge_error, :string
diff --git a/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb b/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb
index 8d47dac6441e87339cbe3a0a710d1f9e9a3391b2..229c9942b5031c02d111381fcc1aebf6a53991a9 100644
--- a/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb
+++ b/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddNullToNameForCiProjects < ActiveRecord::Migration
   def up
     change_column_null :ci_projects, :name, true
diff --git a/db/migrate/20150930110012_add_group_share_lock.rb b/db/migrate/20150930110012_add_group_share_lock.rb
index 78d1a4538f250870e944f4a6fc559687dc917428..96938bf9ab64b8ff5810a771e4e859aae477b47f 100644
--- a/db/migrate/20150930110012_add_group_share_lock.rb
+++ b/db/migrate/20150930110012_add_group_share_lock.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddGroupShareLock < ActiveRecord::Migration
   def change
     add_column :namespaces, :share_with_group_lock, :boolean, default: false
diff --git a/db/migrate/20151002112914_add_stage_idx_to_builds.rb b/db/migrate/20151002112914_add_stage_idx_to_builds.rb
index 68a745ffef49d6c1839964560013d6d274ff9434..4297ba0e7c81f4ca1d4718849fdc972625d3716e 100644
--- a/db/migrate/20151002112914_add_stage_idx_to_builds.rb
+++ b/db/migrate/20151002112914_add_stage_idx_to_builds.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddStageIdxToBuilds < ActiveRecord::Migration
   def change
     add_column :ci_builds, :stage_idx, :integer
diff --git a/db/migrate/20151002121400_add_index_for_builds.rb b/db/migrate/20151002121400_add_index_for_builds.rb
index 4ffc1363910dab6f9e4443d06d2b1146d5f18136..bd945c54540a9f306befd2ca231c18dd7f98927a 100644
--- a/db/migrate/20151002121400_add_index_for_builds.rb
+++ b/db/migrate/20151002121400_add_index_for_builds.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddIndexForBuilds < ActiveRecord::Migration
   def up
     add_index :ci_builds, [:commit_id, :stage_idx, :created_at]
diff --git a/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb b/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb
index e3d2ac1cea5cc43e34d52e07a1a055b5dbc20693..3c0fcf6c45d3b4602e3b2477bb8f10cc610827f4 100644
--- a/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb
+++ b/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddRefAndTagToBuilds < ActiveRecord::Migration
   def change
     add_column :ci_builds, :tag, :boolean
diff --git a/db/migrate/20151002122943_migrate_ref_and_tag_to_build.rb b/db/migrate/20151002122943_migrate_ref_and_tag_to_build.rb
index 01d7b3f6773822dcbae7116c76d0c3ff90acceb6..52217ce5af2c04de40e233133d09f7406681b915 100644
--- a/db/migrate/20151002122943_migrate_ref_and_tag_to_build.rb
+++ b/db/migrate/20151002122943_migrate_ref_and_tag_to_build.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class MigrateRefAndTagToBuild < ActiveRecord::Migration
   def change
     execute('UPDATE ci_builds SET ref=(SELECT ref FROM ci_commits WHERE ci_commits.id = ci_builds.commit_id) WHERE ref IS NULL')
diff --git a/db/migrate/20151005075649_add_user_id_to_build.rb b/db/migrate/20151005075649_add_user_id_to_build.rb
index 0f4b92b8b79f3d587772627bf6cfea01c7125518..be9d403e002ce59e26ba0b88b84d02829c692c00 100644
--- a/db/migrate/20151005075649_add_user_id_to_build.rb
+++ b/db/migrate/20151005075649_add_user_id_to_build.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddUserIdToBuild < ActiveRecord::Migration
   def change
     add_column :ci_builds, :user_id, :integer
diff --git a/db/migrate/20151005150751_add_layout_option_for_users.rb b/db/migrate/20151005150751_add_layout_option_for_users.rb
index ead9b1f89774579614e0b536ef7d3e94ba8cc71e..7e68606969f29f3dbe6523f90485b0d397c2e554 100644
--- a/db/migrate/20151005150751_add_layout_option_for_users.rb
+++ b/db/migrate/20151005150751_add_layout_option_for_users.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddLayoutOptionForUsers < ActiveRecord::Migration
   def change
     add_column :users, :layout, :integer, default: 0
diff --git a/db/migrate/20151005162154_remove_ci_enabled_from_application_settings.rb b/db/migrate/20151005162154_remove_ci_enabled_from_application_settings.rb
index be6aa810bb5ace751432733fbed51563fe0bcefe..07dba598749d5386f0768d8b521fa6794c1d9c74 100644
--- a/db/migrate/20151005162154_remove_ci_enabled_from_application_settings.rb
+++ b/db/migrate/20151005162154_remove_ci_enabled_from_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveCiEnabledFromApplicationSettings < ActiveRecord::Migration
   def change
     remove_column :application_settings, :ci_enabled, :boolean, null: false, default: true
diff --git a/db/migrate/20151007120511_namespaces_projects_path_lower_indexes.rb b/db/migrate/20151007120511_namespaces_projects_path_lower_indexes.rb
index 7f6cd6d5a78a670a5d0820b3ee881aaa97c8da57..38208e598041a420df60094b6c04d0053857db77 100644
--- a/db/migrate/20151007120511_namespaces_projects_path_lower_indexes.rb
+++ b/db/migrate/20151007120511_namespaces_projects_path_lower_indexes.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class NamespacesProjectsPathLowerIndexes < ActiveRecord::Migration
   disable_ddl_transaction!
 
diff --git a/db/migrate/20151008110232_add_users_lower_username_email_indexes.rb b/db/migrate/20151008110232_add_users_lower_username_email_indexes.rb
index 2f2dc7767855c9afd0dc85bf7429f09b2af9b2da..6080d2a0fcfa9eb9ad02a624606ae3b720485341 100644
--- a/db/migrate/20151008110232_add_users_lower_username_email_indexes.rb
+++ b/db/migrate/20151008110232_add_users_lower_username_email_indexes.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddUsersLowerUsernameEmailIndexes < ActiveRecord::Migration
   disable_ddl_transaction!
 
diff --git a/db/migrate/20151008123042_add_type_and_description_to_builds.rb b/db/migrate/20151008123042_add_type_and_description_to_builds.rb
index c72b1c611c6db7125921b5f2aebe8f3aa884661e..a19eb6c6c49bb30c3a42d2a13bd8c19022c3b63b 100644
--- a/db/migrate/20151008123042_add_type_and_description_to_builds.rb
+++ b/db/migrate/20151008123042_add_type_and_description_to_builds.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddTypeAndDescriptionToBuilds < ActiveRecord::Migration
   def change
     add_column :ci_builds, :type, :string
diff --git a/db/migrate/20151008130321_migrate_name_to_description_for_builds.rb b/db/migrate/20151008130321_migrate_name_to_description_for_builds.rb
index f5c44babd84277d0b7764942a114c54f7e1b32fb..306fa7092ea7f879e06ba8ebbeb99499ccfe0c9a 100644
--- a/db/migrate/20151008130321_migrate_name_to_description_for_builds.rb
+++ b/db/migrate/20151008130321_migrate_name_to_description_for_builds.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class MigrateNameToDescriptionForBuilds < ActiveRecord::Migration
   def change
     execute("UPDATE ci_builds SET type='Ci::Build' WHERE type IS NULL")
diff --git a/db/migrate/20151008143519_add_admin_notification_email_setting.rb b/db/migrate/20151008143519_add_admin_notification_email_setting.rb
index 0bb581efe2c56d70d3041c17c065b7e631b00a11..f48ec9aa4a6daa062e923f5c556675fc8aac7305 100644
--- a/db/migrate/20151008143519_add_admin_notification_email_setting.rb
+++ b/db/migrate/20151008143519_add_admin_notification_email_setting.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddAdminNotificationEmailSetting < ActiveRecord::Migration
   def change
     add_column :application_settings, :admin_notification_email, :string
diff --git a/db/migrate/20151012173029_set_jira_service_api_url.rb b/db/migrate/20151012173029_set_jira_service_api_url.rb
index 2af99e0db0b3f3624b7ef7866609a247357addc3..2b6f61428c013c9b4998de3bf64371cb383e2f2d 100644
--- a/db/migrate/20151012173029_set_jira_service_api_url.rb
+++ b/db/migrate/20151012173029_set_jira_service_api_url.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class SetJiraServiceApiUrl < ActiveRecord::Migration
   # This migration can be performed online without errors, but some Jira API calls may be missed
   # when doing so because api_url is not yet available.
diff --git a/db/migrate/20151013092124_add_artifacts_file_to_builds.rb b/db/migrate/20151013092124_add_artifacts_file_to_builds.rb
index 5a299f7b26dd011909c83d5aea552f6bc1460d74..a54ac9d57a4832bcb3080afbbc89ee49b7d5bd88 100644
--- a/db/migrate/20151013092124_add_artifacts_file_to_builds.rb
+++ b/db/migrate/20151013092124_add_artifacts_file_to_builds.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddArtifactsFileToBuilds < ActiveRecord::Migration
   def change
     add_column :ci_builds, :artifacts_file, :text
diff --git a/db/migrate/20151016131433_add_ci_projects_gl_project_id_index.rb b/db/migrate/20151016131433_add_ci_projects_gl_project_id_index.rb
index 52a47aa9c54debafcad6fd7e5936b6380a01cf48..eb3351eb767eb88ce79a9907e1bfeca496211f44 100644
--- a/db/migrate/20151016131433_add_ci_projects_gl_project_id_index.rb
+++ b/db/migrate/20151016131433_add_ci_projects_gl_project_id_index.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddCiProjectsGlProjectIdIndex < ActiveRecord::Migration
   def change
     add_index :ci_commits, :gl_project_id
diff --git a/db/migrate/20151016195451_add_ci_builds_and_projects_indexes.rb b/db/migrate/20151016195451_add_ci_builds_and_projects_indexes.rb
index 7f1af1c758307f65a568079ddbe7aa9e635cc4e2..899e004d61018db427b365e8cc3543f815225a20 100644
--- a/db/migrate/20151016195451_add_ci_builds_and_projects_indexes.rb
+++ b/db/migrate/20151016195451_add_ci_builds_and_projects_indexes.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddCiBuildsAndProjectsIndexes < ActiveRecord::Migration
   def change
     add_index :ci_projects, :gitlab_id
diff --git a/db/migrate/20151016195706_add_notes_line_code_index.rb b/db/migrate/20151016195706_add_notes_line_code_index.rb
index aeeb1a759fab2aef1b184d24b683052647c643b2..3298630c1e8b95e41ae166c9c104a459530495c7 100644
--- a/db/migrate/20151016195706_add_notes_line_code_index.rb
+++ b/db/migrate/20151016195706_add_notes_line_code_index.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddNotesLineCodeIndex < ActiveRecord::Migration
   def change
     add_index :notes, :line_code
diff --git a/db/migrate/20151019111551_fix_build_tags.rb b/db/migrate/20151019111551_fix_build_tags.rb
index 299a24b0a7c5a75ce171f4cd5bf5765ee0336ac2..8c05acfc1904171856981db48ffee0d699625543 100644
--- a/db/migrate/20151019111551_fix_build_tags.rb
+++ b/db/migrate/20151019111551_fix_build_tags.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class FixBuildTags < ActiveRecord::Migration
   def up
     execute("UPDATE taggings SET taggable_type='CommitStatus' WHERE taggable_type='Ci::Build'")
diff --git a/db/migrate/20151019111703_fail_build_without_names.rb b/db/migrate/20151019111703_fail_build_without_names.rb
index dcdb5d1b25ddcd5adb0744ce9d143b8814d4ea02..362e31eb4354fb06c34b0c7ead958e6ced23b164 100644
--- a/db/migrate/20151019111703_fail_build_without_names.rb
+++ b/db/migrate/20151019111703_fail_build_without_names.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class FailBuildWithoutNames < ActiveRecord::Migration
   def up
     execute("UPDATE ci_builds SET status='failed' WHERE name IS NULL AND status='pending'")
diff --git a/db/migrate/20151020145526_add_services_template_index.rb b/db/migrate/20151020145526_add_services_template_index.rb
index 1b04f313565dafc7327403381a509bbefd129d05..14ff07bd726572c541a4a81503e1d4edf55a2109 100644
--- a/db/migrate/20151020145526_add_services_template_index.rb
+++ b/db/migrate/20151020145526_add_services_template_index.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddServicesTemplateIndex < ActiveRecord::Migration
   def change
     add_index :services, :template
diff --git a/db/migrate/20151020173516_ci_limits_to_mysql.rb b/db/migrate/20151020173516_ci_limits_to_mysql.rb
index 9bb960082f58befe42999d26e654750eae9db8c9..5314611cbcd1e2bb557a1054ab8d757b8bf803ac 100644
--- a/db/migrate/20151020173516_ci_limits_to_mysql.rb
+++ b/db/migrate/20151020173516_ci_limits_to_mysql.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CiLimitsToMysql < ActiveRecord::Migration
   def change
     return unless ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/
diff --git a/db/migrate/20151020173906_add_ci_builds_index_for_status.rb b/db/migrate/20151020173906_add_ci_builds_index_for_status.rb
index c3f0e0606dab6b46be92e8993a3b815aea52f4af..81a31e46ff88f95b349215bf6b3f84bc860718d4 100644
--- a/db/migrate/20151020173906_add_ci_builds_index_for_status.rb
+++ b/db/migrate/20151020173906_add_ci_builds_index_for_status.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddCiBuildsIndexForStatus < ActiveRecord::Migration
   def change
     add_index :ci_builds, [:commit_id, :status, :type]
diff --git a/db/migrate/20151023112551_fail_build_with_empty_name.rb b/db/migrate/20151023112551_fail_build_with_empty_name.rb
index 41c0f0649cd6192211e9a219abf24eba2f562fd0..0666dfeaef4105e376b5920176822d239d76bd0f 100644
--- a/db/migrate/20151023112551_fail_build_with_empty_name.rb
+++ b/db/migrate/20151023112551_fail_build_with_empty_name.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class FailBuildWithEmptyName < ActiveRecord::Migration
   def up
     execute("UPDATE ci_builds SET status='failed' WHERE (name IS NULL OR name='') AND status='pending'")
diff --git a/db/migrate/20151023144219_remove_satellites.rb b/db/migrate/20151023144219_remove_satellites.rb
index e73f300028ac8a2f2c47d0b9ae5e42c16e286834..98fe0bd7d1d5f042505b3e41e41a516980a389eb 100644
--- a/db/migrate/20151023144219_remove_satellites.rb
+++ b/db/migrate/20151023144219_remove_satellites.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 require 'fileutils'
 
 class RemoveSatellites < ActiveRecord::Migration
diff --git a/db/migrate/20151026182941_add_project_path_index.rb b/db/migrate/20151026182941_add_project_path_index.rb
index a62fe199d70e75349d4b573785396c7f72eb192c..117f65c1a1b2dd803beb82ce05e922a356476168 100644
--- a/db/migrate/20151026182941_add_project_path_index.rb
+++ b/db/migrate/20151026182941_add_project_path_index.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddProjectPathIndex < ActiveRecord::Migration
   def up
     add_index :projects, :path
diff --git a/db/migrate/20151028152939_add_merge_when_build_succeeds_to_merge_request.rb b/db/migrate/20151028152939_add_merge_when_build_succeeds_to_merge_request.rb
index ceb52f0c2224752e070b03387e7b962e39047766..4a989669464af9daf65f7d5129f0516dd9bc673e 100644
--- a/db/migrate/20151028152939_add_merge_when_build_succeeds_to_merge_request.rb
+++ b/db/migrate/20151028152939_add_merge_when_build_succeeds_to_merge_request.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddMergeWhenBuildSucceedsToMergeRequest < ActiveRecord::Migration
   def change
     add_column :merge_requests, :merge_params, :text
diff --git a/db/migrate/20151103001141_add_public_to_group.rb b/db/migrate/20151103001141_add_public_to_group.rb
index 635346300c2d17fa92adedf866f489e8ec4aebd3..ba1f7c27832d1690c4233d9f65f324bdc7b7abd2 100644
--- a/db/migrate/20151103001141_add_public_to_group.rb
+++ b/db/migrate/20151103001141_add_public_to_group.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddPublicToGroup < ActiveRecord::Migration
   def change
     add_column :namespaces, :public, :boolean, default: false
diff --git a/db/migrate/20151103133339_add_shared_runners_setting.rb b/db/migrate/20151103133339_add_shared_runners_setting.rb
index 4231dfd5c2e81a00f4ffd2f41acf00dd5fc59137..b5b34d4ca613348b2e4edda43313dc275ced072e 100644
--- a/db/migrate/20151103133339_add_shared_runners_setting.rb
+++ b/db/migrate/20151103133339_add_shared_runners_setting.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddSharedRunnersSetting < ActiveRecord::Migration
   def up
     add_column :application_settings, :shared_runners_enabled, :boolean, default: true, null: false
diff --git a/db/migrate/20151103134857_create_lfs_objects.rb b/db/migrate/20151103134857_create_lfs_objects.rb
index 2d04c170a88c9f9c0eb4d5bcb2392b5b7d50ff66..745b52e2b24bf52bbb4960eec8be439956490d65 100644
--- a/db/migrate/20151103134857_create_lfs_objects.rb
+++ b/db/migrate/20151103134857_create_lfs_objects.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateLfsObjects < ActiveRecord::Migration
   def change
     create_table :lfs_objects do |t|
diff --git a/db/migrate/20151103134958_create_lfs_objects_projects.rb b/db/migrate/20151103134958_create_lfs_objects_projects.rb
index f3f58b931ece32a085ab123006f210e6727fb0b4..3178e85b899b16167e53a9a95f8c4591ef41f2f0 100644
--- a/db/migrate/20151103134958_create_lfs_objects_projects.rb
+++ b/db/migrate/20151103134958_create_lfs_objects_projects.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateLfsObjectsProjects < ActiveRecord::Migration
   def change
     create_table :lfs_objects_projects do |t|
diff --git a/db/migrate/20151104105513_add_file_to_lfs_objects.rb b/db/migrate/20151104105513_add_file_to_lfs_objects.rb
index 7c57f3f0df655017afcd6ed4acba5af3b415973a..4e46ae8101c22f04b5a448d2c10ced1caacf3422 100644
--- a/db/migrate/20151104105513_add_file_to_lfs_objects.rb
+++ b/db/migrate/20151104105513_add_file_to_lfs_objects.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddFileToLfsObjects < ActiveRecord::Migration
   def change
     add_column :lfs_objects, :file, :string
diff --git a/db/migrate/20151105094515_create_releases.rb b/db/migrate/20151105094515_create_releases.rb
index fe4608c6662a27da1bba76be77729dce46d88f19..145b8db1486177f06cb972235e60db6e086ac64f 100644
--- a/db/migrate/20151105094515_create_releases.rb
+++ b/db/migrate/20151105094515_create_releases.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateReleases < ActiveRecord::Migration
   def change
     create_table :releases do |t|
diff --git a/db/migrate/20151106000015_add_is_award_to_notes.rb b/db/migrate/20151106000015_add_is_award_to_notes.rb
index 02b271637e960c9214481e0e4eed4287f49d81c0..b463d939b78d44b9be049bd85ac1dbbe4163f6ab 100644
--- a/db/migrate/20151106000015_add_is_award_to_notes.rb
+++ b/db/migrate/20151106000015_add_is_award_to_notes.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddIsAwardToNotes < ActiveRecord::Migration
   def change
     add_column :notes, :is_award, :boolean, default: false, null: false
diff --git a/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb b/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb
index 01d8c0f043eb7f1c4987056d87b4f64b1581dea3..25106ace7e92fc5709059d2ba93f021945fee593 100644
--- a/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb
+++ b/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddMaxArtifactsSizeToApplicationSettings < ActiveRecord::Migration
   def change
     add_column :application_settings, :max_artifacts_size, :integer, default: 100, null: false
diff --git a/db/migrate/20151109134526_add_issues_state_index.rb b/db/migrate/20151109134526_add_issues_state_index.rb
index 1c4d2e30171886aadd04fd34f482843d9ca9d7aa..7a9970e85914eccf85823c57d1e2f47a3adba6af 100644
--- a/db/migrate/20151109134526_add_issues_state_index.rb
+++ b/db/migrate/20151109134526_add_issues_state_index.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddIssuesStateIndex < ActiveRecord::Migration
   def change
     add_index :issues, :state
diff --git a/db/migrate/20151109134916_add_projects_visibility_level_index.rb b/db/migrate/20151109134916_add_projects_visibility_level_index.rb
index 600b4bafd98482679edca82ecdde5624ffcdd679..471db437b111ee9e930bd4843456a934435b51e7 100644
--- a/db/migrate/20151109134916_add_projects_visibility_level_index.rb
+++ b/db/migrate/20151109134916_add_projects_visibility_level_index.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddProjectsVisibilityLevelIndex < ActiveRecord::Migration
   def change
     add_index :projects, :visibility_level
diff --git a/db/migrate/20151110125604_add_import_error_to_project.rb b/db/migrate/20151110125604_add_import_error_to_project.rb
index 7fc990f8d0a9f56316c566ae421b158e8d26d268..793358c305e9276293bc3256eff0ff9107bbc34e 100644
--- a/db/migrate/20151110125604_add_import_error_to_project.rb
+++ b/db/migrate/20151110125604_add_import_error_to_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddImportErrorToProject < ActiveRecord::Migration
   def change
     add_column :projects, :import_error, :text
diff --git a/db/migrate/20151113115819_canonicalize_kerberos_identities.rb b/db/migrate/20151113115819_canonicalize_kerberos_identities.rb
index 2ec893aa35c18bd335a3d7060080ab2530c5ced0..098cab723e987293a6420d2cbc510980bace6b33 100644
--- a/db/migrate/20151113115819_canonicalize_kerberos_identities.rb
+++ b/db/migrate/20151113115819_canonicalize_kerberos_identities.rb
@@ -12,7 +12,7 @@ class CanonicalizeKerberosIdentities < ActiveRecord::Migration
       default_realm = krb5.get_default_realm
       krb5.close # release memory allocated by the krb5 library
       default_realm || ''
-    rescue Exception
+    rescue StandardError
       '' # could not find the system's default realm, maybe there's no Kerberos at all
     end
   end
diff --git a/db/migrate/20151114113410_add_index_for_lfs_oid_and_size.rb b/db/migrate/20151114113410_add_index_for_lfs_oid_and_size.rb
index d10f1f6e6054ab5f5561082fe95b0039695f8aa1..00a4c74ffbcc0a58af4e7a56c750bf01ccbc95d3 100644
--- a/db/migrate/20151114113410_add_index_for_lfs_oid_and_size.rb
+++ b/db/migrate/20151114113410_add_index_for_lfs_oid_and_size.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddIndexForLfsOidAndSize < ActiveRecord::Migration
   def change
     add_index :lfs_objects, :oid
diff --git a/db/migrate/20151116144118_add_unique_for_lfs_oid_index.rb b/db/migrate/20151116144118_add_unique_for_lfs_oid_index.rb
index 41b93da0a8699036002deea43de92c2cb2bf2ad5..1f192544ea155d9e04094c126d1f2b02ac75b772 100644
--- a/db/migrate/20151116144118_add_unique_for_lfs_oid_index.rb
+++ b/db/migrate/20151116144118_add_unique_for_lfs_oid_index.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddUniqueForLfsOidIndex < ActiveRecord::Migration
   def change
     remove_index :lfs_objects, :oid
diff --git a/db/migrate/20151118162244_add_projects_public_index.rb b/db/migrate/20151118162244_add_projects_public_index.rb
index fded70e3c0c8c38c67f7d30e4be82d11039be991..589f124c21e674c8bd2fae0f4dad5201b1b5d6a0 100644
--- a/db/migrate/20151118162244_add_projects_public_index.rb
+++ b/db/migrate/20151118162244_add_projects_public_index.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddProjectsPublicIndex < ActiveRecord::Migration
   def change
     add_index :namespaces, :public
diff --git a/db/migrate/20151201203948_raise_hook_url_limit.rb b/db/migrate/20151201203948_raise_hook_url_limit.rb
index 98a7fca6f6f3eb256ffd78c312a72dd244361d8e..c490b7ace0f58a5afb1dbb16f6db021a0a3d3b77 100644
--- a/db/migrate/20151201203948_raise_hook_url_limit.rb
+++ b/db/migrate/20151201203948_raise_hook_url_limit.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RaiseHookUrlLimit < ActiveRecord::Migration
   def change
     change_column :web_hooks, :url, :string, limit: 2000
diff --git a/db/migrate/20151203162133_add_hide_project_limit_to_users.rb b/db/migrate/20151203162133_add_hide_project_limit_to_users.rb
index 6ffadfa1894ee71c746cde101e7d95fa26e48682..5dc6d8bf44527f34fe9708e7c7cfd9a85c88ac7f 100644
--- a/db/migrate/20151203162133_add_hide_project_limit_to_users.rb
+++ b/db/migrate/20151203162133_add_hide_project_limit_to_users.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddHideProjectLimitToUsers < ActiveRecord::Migration
   def change
     add_column :users, :hide_project_limit, :boolean, default: false
diff --git a/db/migrate/20151203162134_add_build_events_to_services.rb b/db/migrate/20151203162134_add_build_events_to_services.rb
index c5542cb864da8ca5198f8d76dd8ff6fe61d991b5..455882e5ec06b69626fe6588cff9ee6620cab1ba 100644
--- a/db/migrate/20151203162134_add_build_events_to_services.rb
+++ b/db/migrate/20151203162134_add_build_events_to_services.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddBuildEventsToServices < ActiveRecord::Migration
   def change
     add_column :services, :build_events, :boolean, default: false, null: false
diff --git a/db/migrate/20151209144329_migrate_ci_web_hooks.rb b/db/migrate/20151209144329_migrate_ci_web_hooks.rb
index d7e196e6763d442f4e650584f0378cb770d75c57..cb1e556623a62c9bfe1b19b95c3a3d2f35ea207c 100644
--- a/db/migrate/20151209144329_migrate_ci_web_hooks.rb
+++ b/db/migrate/20151209144329_migrate_ci_web_hooks.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class MigrateCiWebHooks < ActiveRecord::Migration
   include Gitlab::Database
 
diff --git a/db/migrate/20151209145909_migrate_ci_emails.rb b/db/migrate/20151209145909_migrate_ci_emails.rb
index 7f330a2cf0a0fbab8f0a9a6638a0e2596b78cc2a..6b7a106814d2c7f2cd01872973fac885f8d65421 100644
--- a/db/migrate/20151209145909_migrate_ci_emails.rb
+++ b/db/migrate/20151209145909_migrate_ci_emails.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class MigrateCiEmails < ActiveRecord::Migration
   include Gitlab::Database
 
diff --git a/db/migrate/20151210030143_add_unlock_token_to_user.rb b/db/migrate/20151210030143_add_unlock_token_to_user.rb
index 0ea66ba65dfa6df8896b219dcedbb9aac460b9cd..d23c648f7820be78ffa1adefd59b7cf6bb2cd2a4 100644
--- a/db/migrate/20151210030143_add_unlock_token_to_user.rb
+++ b/db/migrate/20151210030143_add_unlock_token_to_user.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddUnlockTokenToUser < ActiveRecord::Migration
   def change
     add_column :users, :unlock_token, :string
diff --git a/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb b/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb
index 00f88180e46ab07536689ad373ea221e213e1037..92c7b5befd2578b2b501817929a0d24fa6e9b274 100644
--- a/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb
+++ b/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddRunnersRegistrationTokenToApplicationSettings < ActiveRecord::Migration
   def change
     add_column :application_settings, :runners_registration_token, :string
diff --git a/db/migrate/20151210125232_migrate_ci_slack_service.rb b/db/migrate/20151210125232_migrate_ci_slack_service.rb
index f14efa3e95d398c198ae587665c2f7dba94e5ba1..633d5148d979d2883f7cc0c8cbe96af151771a3d 100644
--- a/db/migrate/20151210125232_migrate_ci_slack_service.rb
+++ b/db/migrate/20151210125232_migrate_ci_slack_service.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class MigrateCiSlackService < ActiveRecord::Migration
   include Gitlab::Database
 
diff --git a/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb b/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb
index b9e04323576eabe1d50b306d032cf6763c244142..dae084ce180905bec1767cd2285a6b3bcb9b6b55 100644
--- a/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb
+++ b/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class MigrateCiHipChatService < ActiveRecord::Migration
   include Gitlab::Database
 
diff --git a/db/migrate/20151210125928_add_ci_to_project.rb b/db/migrate/20151210125928_add_ci_to_project.rb
index 8c167f64a2b59444c94a5b8a1f914a252e44aec1..a9ff49a3f7e1d384f5b1ea9b049c8034d176799f 100644
--- a/db/migrate/20151210125928_add_ci_to_project.rb
+++ b/db/migrate/20151210125928_add_ci_to_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddCiToProject < ActiveRecord::Migration
   def change
     add_column :projects, :ci_id, :integer
diff --git a/db/migrate/20151210125929_add_project_id_to_ci.rb b/db/migrate/20151210125929_add_project_id_to_ci.rb
index 84273591fa2ede4f606e2ab9cae2418330e00cf1..b5de64b82ca8cc4c9ee38a06e7ab0c0a40eff500 100644
--- a/db/migrate/20151210125929_add_project_id_to_ci.rb
+++ b/db/migrate/20151210125929_add_project_id_to_ci.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddProjectIdToCi < ActiveRecord::Migration
   def change
     add_column :ci_builds, :gl_project_id, :integer
diff --git a/db/migrate/20151210125930_migrate_ci_to_project.rb b/db/migrate/20151210125930_migrate_ci_to_project.rb
index c32c7feb1931443f5e28b1ee0d56eae5cbe71674..bb6d74ae212f5a7d21de4471defcff7879759ef5 100644
--- a/db/migrate/20151210125930_migrate_ci_to_project.rb
+++ b/db/migrate/20151210125930_migrate_ci_to_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class MigrateCiToProject < ActiveRecord::Migration
   def up
     migrate_project_id_for_table('ci_runner_projects')
diff --git a/db/migrate/20151210125931_add_index_to_ci_tables.rb b/db/migrate/20151210125931_add_index_to_ci_tables.rb
index 5e129c9303d9b0bece0d8fc36100682f8a8a969f..d87d335cf6b1093af2f3fd9413b5ce321ec2cb0a 100644
--- a/db/migrate/20151210125931_add_index_to_ci_tables.rb
+++ b/db/migrate/20151210125931_add_index_to_ci_tables.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddIndexToCiTables < ActiveRecord::Migration
   def change
     add_index :ci_builds, :gl_project_id
diff --git a/db/migrate/20151210125932_drop_null_for_ci_tables.rb b/db/migrate/20151210125932_drop_null_for_ci_tables.rb
index c520c2ed56f67e2f08d91fa29174d12bcb413ece..e1a0a964589adad0e6e766a303bfe837ca8b4eb9 100644
--- a/db/migrate/20151210125932_drop_null_for_ci_tables.rb
+++ b/db/migrate/20151210125932_drop_null_for_ci_tables.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class DropNullForCiTables < ActiveRecord::Migration
   def change
     remove_index :ci_variables, :project_id
diff --git a/db/migrate/20151218154042_add_tfa_to_application_settings.rb b/db/migrate/20151218154042_add_tfa_to_application_settings.rb
index dd95db775c5e5e548b8d567223fd8c9dc2607de0..afdaf76b917502c1420ed1c0503ab92f0fd1ff4f 100644
--- a/db/migrate/20151218154042_add_tfa_to_application_settings.rb
+++ b/db/migrate/20151218154042_add_tfa_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddTfaToApplicationSettings < ActiveRecord::Migration
   def change
     change_table :application_settings do |t|
diff --git a/db/migrate/20151221234414_add_tfa_additional_fields.rb b/db/migrate/20151221234414_add_tfa_additional_fields.rb
index c16df47932f6e902d58e5758bc5ff86047cfdfc8..c3e4aaa606a8f55b28ed67ae1c04006253e1d102 100644
--- a/db/migrate/20151221234414_add_tfa_additional_fields.rb
+++ b/db/migrate/20151221234414_add_tfa_additional_fields.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddTfaAdditionalFields < ActiveRecord::Migration
   def change
     change_table :users do |t|
diff --git a/db/migrate/20151224123230_rename_emojis.rb b/db/migrate/20151224123230_rename_emojis.rb
index 62d921dfdcce3e26b7efd0afd2a3dd192bddeead..2c24f3beeea492d3d85ed29ab348fb6dcda84f8c 100644
--- a/db/migrate/20151224123230_rename_emojis.rb
+++ b/db/migrate/20151224123230_rename_emojis.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 # Migration type: online without errors (works on previous version and new one)
 class RenameEmojis < ActiveRecord::Migration
   def up
diff --git a/db/migrate/20151228111122_remove_public_from_namespace.rb b/db/migrate/20151228111122_remove_public_from_namespace.rb
index f4c848bbf47c68be7fcbf15d4269ef08c0a28ec3..bcb322d9cba3441fd9625ea09b481eb847b84969 100644
--- a/db/migrate/20151228111122_remove_public_from_namespace.rb
+++ b/db/migrate/20151228111122_remove_public_from_namespace.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 # Migration type: online
 class RemovePublicFromNamespace < ActiveRecord::Migration
   def change
diff --git a/db/migrate/20151228150906_influxdb_settings.rb b/db/migrate/20151228150906_influxdb_settings.rb
index 3012bd52cfdd96c09ed4bd9cdd13996e41e65d1b..2e080a02e6a8c5b62b99ac9b3c8c113951f9b883 100644
--- a/db/migrate/20151228150906_influxdb_settings.rb
+++ b/db/migrate/20151228150906_influxdb_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class InfluxdbSettings < ActiveRecord::Migration
   def change
     add_column :application_settings, :metrics_enabled, :boolean, default: false
diff --git a/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb b/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb
index 259fd0248d2914c52f95a8353668458b8d0d9ce7..e0dd19b2b0600ff0bc9a95f4656d558f00fe8fdf 100644
--- a/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb
+++ b/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddRecaptchaToApplicationSettings < ActiveRecord::Migration
   def change
     change_table :application_settings do |t|
diff --git a/db/migrate/20151229102248_influxdb_udp_port_setting.rb b/db/migrate/20151229102248_influxdb_udp_port_setting.rb
index ae0499f936d6fcfd719c3c231e2ecabf975c4f26..3e1bfd438999f9ed803ad2bbaa87c280ee3119be 100644
--- a/db/migrate/20151229102248_influxdb_udp_port_setting.rb
+++ b/db/migrate/20151229102248_influxdb_udp_port_setting.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class InfluxdbUdpPortSetting < ActiveRecord::Migration
   def change
     add_column :application_settings, :metrics_port, :integer, default: 8089
diff --git a/db/migrate/20151229112614_influxdb_remote_database_setting.rb b/db/migrate/20151229112614_influxdb_remote_database_setting.rb
index f0e1ee1e7a79a0d3720160796fc1ffa389039fc8..d2ac906ead335af0c367d67cdea5e8b0918641ec 100644
--- a/db/migrate/20151229112614_influxdb_remote_database_setting.rb
+++ b/db/migrate/20151229112614_influxdb_remote_database_setting.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class InfluxdbRemoteDatabaseSetting < ActiveRecord::Migration
   def change
     remove_column :application_settings, :metrics_database
diff --git a/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb b/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb
index 6c282fc50394eb6daa761c1344acd338c94848d5..4fcca06d905a55ae7b2fd1e68685e4b78f85a95c 100644
--- a/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb
+++ b/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddArtifactsMetadataToCiBuild < ActiveRecord::Migration
   def change
     add_column :ci_builds, :artifacts_metadata, :text
diff --git a/db/migrate/20151231152326_add_akismet_to_application_settings.rb b/db/migrate/20151231152326_add_akismet_to_application_settings.rb
index 3f52c758f9a9a59262efadac25acc08696d6e4d7..7b0fab6f557f819d83f3e7df73dee69f8b22d00a 100644
--- a/db/migrate/20151231152326_add_akismet_to_application_settings.rb
+++ b/db/migrate/20151231152326_add_akismet_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddAkismetToApplicationSettings < ActiveRecord::Migration
   def change
     change_table :application_settings do |t|
diff --git a/db/migrate/20151231202530_remove_alert_type_from_broadcast_messages.rb b/db/migrate/20151231202530_remove_alert_type_from_broadcast_messages.rb
index 78fdfeaf5cf9a2ef0cbc90b3e043970cf3c4c077..0bdd639eb214060dddb939e225c8c923b98f22d5 100644
--- a/db/migrate/20151231202530_remove_alert_type_from_broadcast_messages.rb
+++ b/db/migrate/20151231202530_remove_alert_type_from_broadcast_messages.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveAlertTypeFromBroadcastMessages < ActiveRecord::Migration
   def change
     remove_column :broadcast_messages, :alert_type, :integer
diff --git a/db/migrate/20160106162223_add_index_milestones_title.rb b/db/migrate/20160106162223_add_index_milestones_title.rb
index 767885e2aacb3b3b4143abf7ed31abddca039651..9b9b6445a08392371c2a80dee043f967368fb54e 100644
--- a/db/migrate/20160106162223_add_index_milestones_title.rb
+++ b/db/migrate/20160106162223_add_index_milestones_title.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddIndexMilestonesTitle < ActiveRecord::Migration
   def change
     add_index :milestones, :title
diff --git a/db/migrate/20160106164438_remove_influxdb_credentials.rb b/db/migrate/20160106164438_remove_influxdb_credentials.rb
index 47e74400b97af7cc8c74bf83b63347ef370046c6..987d75d6fdab412f16ff02e014a299b0d131d196 100644
--- a/db/migrate/20160106164438_remove_influxdb_credentials.rb
+++ b/db/migrate/20160106164438_remove_influxdb_credentials.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveInfluxdbCredentials < ActiveRecord::Migration
   def change
     remove_column :application_settings, :metrics_username, :string
diff --git a/db/migrate/20160109054846_create_spam_logs.rb b/db/migrate/20160109054846_create_spam_logs.rb
index f12fe9f8f788e00fa5af20d5e119e9fe2949dc0b..f710327663919856063098ff06e013db5f00f605 100644
--- a/db/migrate/20160109054846_create_spam_logs.rb
+++ b/db/migrate/20160109054846_create_spam_logs.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateSpamLogs < ActiveRecord::Migration
   def change
     create_table :spam_logs do |t|
diff --git a/db/migrate/20160113111034_add_metrics_sample_interval.rb b/db/migrate/20160113111034_add_metrics_sample_interval.rb
index b741f5d2c758db6ff093b3d55e995458f8d50fbb..c1041da818c2a9285503e11005aa8eabf78cc2d0 100644
--- a/db/migrate/20160113111034_add_metrics_sample_interval.rb
+++ b/db/migrate/20160113111034_add_metrics_sample_interval.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddMetricsSampleInterval < ActiveRecord::Migration
   def change
     add_column :application_settings, :metrics_sample_interval, :integer,
diff --git a/db/migrate/20160118155830_add_sentry_to_application_settings.rb b/db/migrate/20160118155830_add_sentry_to_application_settings.rb
index fa7ff9d92289c54297abc4f1424f21ea0d3b5a03..a6f715263efcf7f1e26c82f1671af68b84c2b462 100644
--- a/db/migrate/20160118155830_add_sentry_to_application_settings.rb
+++ b/db/migrate/20160118155830_add_sentry_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddSentryToApplicationSettings < ActiveRecord::Migration
   def change
     change_table :application_settings do |t|
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
index 26606b10b54d66a6d02090892c9f34d6afefdeaa..19ea40b5547e4f76e796c253907e449265410520 100644
--- a/db/migrate/20160118232755_add_ip_blocking_settings_to_application_settings.rb
+++ b/db/migrate/20160118232755_add_ip_blocking_settings_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddIpBlockingSettingsToApplicationSettings < ActiveRecord::Migration
   def change
     add_column :application_settings, :ip_blocking_enabled, :boolean, default: false
diff --git a/db/migrate/20160119111158_add_services_category.rb b/db/migrate/20160119111158_add_services_category.rb
index a9110a8418b5b0394710958535fe82c2acb69cc3..f77484b2f9685fd09ba3d19cff83ac0af61403d6 100644
--- a/db/migrate/20160119111158_add_services_category.rb
+++ b/db/migrate/20160119111158_add_services_category.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddServicesCategory < ActiveRecord::Migration
   def up
     add_column :services, :category, :string, default: 'common', null: false
diff --git a/db/migrate/20160119112418_add_services_default.rb b/db/migrate/20160119112418_add_services_default.rb
index 69a42d7b873a4730f4998cb4b0789372472870b5..7fa531899fedfcf458ccdf6b8a2546b5c0c9d2f1 100644
--- a/db/migrate/20160119112418_add_services_default.rb
+++ b/db/migrate/20160119112418_add_services_default.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddServicesDefault < ActiveRecord::Migration
   def up
     add_column :services, :default, :boolean, default: false
diff --git a/db/migrate/20160119145451_add_ldap_email_to_users.rb b/db/migrate/20160119145451_add_ldap_email_to_users.rb
index 654d31ab15a6dcd7cf50d2eb34bfc78f35625b09..5b2b0bd31cad9eeb83740042d9fc9d0dff95559b 100644
--- a/db/migrate/20160119145451_add_ldap_email_to_users.rb
+++ b/db/migrate/20160119145451_add_ldap_email_to_users.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddLdapEmailToUsers < ActiveRecord::Migration
   def up
     add_column :users, :ldap_email, :boolean, default: false, null: false
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
index d6c6aa4a4e83d112f6ff044f9549139644d4e69e..3837208f81ecd8032ffb1686f55291568203f9bc 100644
--- 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
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddBaseCommitShaToMergeRequestDiffs < ActiveRecord::Migration
   def change
     add_column :merge_request_diffs, :base_commit_sha, :string
diff --git a/db/migrate/20160121030729_add_email_author_in_body_to_application_settings.rb b/db/migrate/20160121030729_add_email_author_in_body_to_application_settings.rb
index d50791410f9333a98cbbef995fdc3ca280db91fa..9a2570ae54486eeb4dc939995bee7bdf42d44d6b 100644
--- a/db/migrate/20160121030729_add_email_author_in_body_to_application_settings.rb
+++ b/db/migrate/20160121030729_add_email_author_in_body_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddEmailAuthorInBodyToApplicationSettings < ActiveRecord::Migration
   def change
     add_column :application_settings, :email_author_in_body, :boolean, default: false
diff --git a/db/migrate/20160122185421_add_pending_delete_to_project.rb b/db/migrate/20160122185421_add_pending_delete_to_project.rb
index 046a5d8fc3214ff219c997fed7b34b52be784f6f..61db852843fe087c85d9cfceff11d3780f05e9df 100644
--- a/db/migrate/20160122185421_add_pending_delete_to_project.rb
+++ b/db/migrate/20160122185421_add_pending_delete_to_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddPendingDeleteToProject < ActiveRecord::Migration
   def change
     add_column :projects, :pending_delete, :boolean, default: false
diff --git a/db/migrate/20160128212447_remove_ip_blocking_settings_from_application_settings.rb b/db/migrate/20160128212447_remove_ip_blocking_settings_from_application_settings.rb
index 41821cdcc42aeef9923d3b623224683f032ac518..60ecda998dd8359fa3433c6ccbda6d6b219928a2 100644
--- a/db/migrate/20160128212447_remove_ip_blocking_settings_from_application_settings.rb
+++ b/db/migrate/20160128212447_remove_ip_blocking_settings_from_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveIpBlockingSettingsFromApplicationSettings < ActiveRecord::Migration
   def change
     remove_column :application_settings, :ip_blocking_enabled, :boolean, default: false
diff --git a/db/migrate/20160128233227_change_lfs_objects_size_column.rb b/db/migrate/20160128233227_change_lfs_objects_size_column.rb
index e7fd1f71777790be3a0faed121fff101207be8d7..645c0cdb192f316b1d61118cf7b998fa17f3d6fe 100644
--- a/db/migrate/20160128233227_change_lfs_objects_size_column.rb
+++ b/db/migrate/20160128233227_change_lfs_objects_size_column.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class ChangeLfsObjectsSizeColumn < ActiveRecord::Migration
   def change
     change_column :lfs_objects, :size, :integer, limit: 8
diff --git a/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb b/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb
index d3ea956952e7d5599af7f64cbddf4b3eb30df02a..b10c0602e249ef9939ab5170fd10c7c28b366f53 100644
--- a/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb
+++ b/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveDotAtomPathEndingOfProjects < ActiveRecord::Migration
   include Gitlab::ShellAdapter
 
diff --git a/db/migrate/20160129155512_add_merge_commit_sha_to_merge_requests.rb b/db/migrate/20160129155512_add_merge_commit_sha_to_merge_requests.rb
index f0d942265148372a5712f337f0cf8ac34a575961..332b5a756e8890af5aa1f3d61664d5857eb23463 100644
--- a/db/migrate/20160129155512_add_merge_commit_sha_to_merge_requests.rb
+++ b/db/migrate/20160129155512_add_merge_commit_sha_to_merge_requests.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddMergeCommitShaToMergeRequests < ActiveRecord::Migration
   def change
     add_column :merge_requests, :merge_commit_sha, :string
diff --git a/db/migrate/20160202091601_add_erasable_to_ci_build.rb b/db/migrate/20160202091601_add_erasable_to_ci_build.rb
index f9912f2274e15c90838a1f3542afd190565ef522..767ae160d0855631a1dcaf620cf4fcd8159d6789 100644
--- a/db/migrate/20160202091601_add_erasable_to_ci_build.rb
+++ b/db/migrate/20160202091601_add_erasable_to_ci_build.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddErasableToCiBuild < ActiveRecord::Migration
   def change
     add_reference :ci_builds, :erased_by, references: :users, index: true
diff --git a/db/migrate/20160202164642_add_allow_guest_to_access_builds_project.rb b/db/migrate/20160202164642_add_allow_guest_to_access_builds_project.rb
index 793984343b4e0bab2a8f6f8dedb8e785cea4a675..2c5cb307fada31aba3a3a2be7a06089577d46f5c 100644
--- a/db/migrate/20160202164642_add_allow_guest_to_access_builds_project.rb
+++ b/db/migrate/20160202164642_add_allow_guest_to_access_builds_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddAllowGuestToAccessBuildsProject < ActiveRecord::Migration
   def change
     add_column :projects, :public_builds, :boolean, default: true, null: false
diff --git a/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb b/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb
index f996ae74dca170a52daf6a89ee504d7045800d03..11b6ff310005ffb089d816c85aac5421feca67e9 100644
--- a/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb
+++ b/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddRealSizeToMergeRequestDiffs < ActiveRecord::Migration
   def change
     add_column :merge_request_diffs, :real_size, :string
diff --git a/db/migrate/20160209130428_add_index_to_snippet.rb b/db/migrate/20160209130428_add_index_to_snippet.rb
index 95d5719be59dc41298b958f07f38501e9bb11e00..4d17c3a2917b1df52338b5f2c1cf24e62737de9c 100644
--- a/db/migrate/20160209130428_add_index_to_snippet.rb
+++ b/db/migrate/20160209130428_add_index_to_snippet.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddIndexToSnippet < ActiveRecord::Migration
   def change
     add_index :snippets, :updated_at
diff --git a/db/migrate/20160212123307_create_tasks.rb b/db/migrate/20160212123307_create_tasks.rb
index c3f6f3abc26f159979fcc9129c0984267b0e5aef..20573b01351acd2184ebafe70d0373ed37e3159d 100644
--- a/db/migrate/20160212123307_create_tasks.rb
+++ b/db/migrate/20160212123307_create_tasks.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateTasks < ActiveRecord::Migration
   def change
     create_table :tasks do |t|
diff --git a/db/migrate/20160217100506_add_description_to_label.rb b/db/migrate/20160217100506_add_description_to_label.rb
index eed6d1f236aa16cc8032c94c01ecf57ae4f4d16b..af5af1674706b5c51a571841b34a2432723764e8 100644
--- a/db/migrate/20160217100506_add_description_to_label.rb
+++ b/db/migrate/20160217100506_add_description_to_label.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddDescriptionToLabel < ActiveRecord::Migration
   def change
     add_column :labels, :description, :string
diff --git a/db/migrate/20160217174422_add_note_to_tasks.rb b/db/migrate/20160217174422_add_note_to_tasks.rb
index da5cb2e05db693fdd8f3b5b72c71eabfac39e53c..a9a2b77e4231c2a581702d1c7823ce81013c20d5 100644
--- a/db/migrate/20160217174422_add_note_to_tasks.rb
+++ b/db/migrate/20160217174422_add_note_to_tasks.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddNoteToTasks < ActiveRecord::Migration
   def change
     add_reference :tasks, :note, index: true
diff --git a/db/migrate/20160220123949_rename_tasks_to_todos.rb b/db/migrate/20160220123949_rename_tasks_to_todos.rb
index 30c10d27146e7a8a356630eadaf33660b7a631a8..f16b37537f3c60b4af4d001274ff5f3da5618eb5 100644
--- a/db/migrate/20160220123949_rename_tasks_to_todos.rb
+++ b/db/migrate/20160220123949_rename_tasks_to_todos.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RenameTasksToTodos < ActiveRecord::Migration
   def change
     rename_table :tasks, :todos
diff --git a/db/migrate/20160222153918_create_appearances_ce.rb b/db/migrate/20160222153918_create_appearances_ce.rb
index bec66bcc71e1931291e3f0f848dc6348fead5bb6..b2d5949b23f52c00a76322685c432da736ee8bb2 100644
--- a/db/migrate/20160222153918_create_appearances_ce.rb
+++ b/db/migrate/20160222153918_create_appearances_ce.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateAppearancesCe < ActiveRecord::Migration
   def change
     unless table_exists?(:appearances)
diff --git a/db/migrate/20160223192159_add_confidential_to_issues.rb b/db/migrate/20160223192159_add_confidential_to_issues.rb
index e9d47fd589aff45f4de9c10db52b066074e05131..5b99ce30e9f06d477693995e2fdab28b2affd44b 100644
--- a/db/migrate/20160223192159_add_confidential_to_issues.rb
+++ b/db/migrate/20160223192159_add_confidential_to_issues.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddConfidentialToIssues < ActiveRecord::Migration
   def change
     add_column :issues, :confidential, :boolean, default: false
diff --git a/db/migrate/20160225090018_add_delete_at_to_issues.rb b/db/migrate/20160225090018_add_delete_at_to_issues.rb
index 3ddbef92978bcad88a87324f5c1fece3e0bf537c..139f911e1c90beb5684de806349790ab7b5bb3a7 100644
--- a/db/migrate/20160225090018_add_delete_at_to_issues.rb
+++ b/db/migrate/20160225090018_add_delete_at_to_issues.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddDeleteAtToIssues < ActiveRecord::Migration
   def change
     add_column :issues, :deleted_at, :datetime
diff --git a/db/migrate/20160225101956_add_delete_at_to_merge_requests.rb b/db/migrate/20160225101956_add_delete_at_to_merge_requests.rb
index 9d09105f17db368ac75750a2c730ba2b2d9e7aa3..4ca3f0dcdc522bc2b47d2ea25641c2292f06d807 100644
--- a/db/migrate/20160225101956_add_delete_at_to_merge_requests.rb
+++ b/db/migrate/20160225101956_add_delete_at_to_merge_requests.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddDeleteAtToMergeRequests < ActiveRecord::Migration
   def change
     add_column :merge_requests, :deleted_at, :datetime
diff --git a/db/migrate/20160226114608_add_trigram_indexes_for_searching.rb b/db/migrate/20160226114608_add_trigram_indexes_for_searching.rb
index d7b00e3d6ed4df6791753c2bdded5f34c5f18a47..375e389e07a2809c21c70c6787bcb0bd26ada8df 100644
--- a/db/migrate/20160226114608_add_trigram_indexes_for_searching.rb
+++ b/db/migrate/20160226114608_add_trigram_indexes_for_searching.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddTrigramIndexesForSearching < ActiveRecord::Migration
   disable_ddl_transaction!
 
diff --git a/db/migrate/20160227120001_add_event_field_for_web_hook.rb b/db/migrate/20160227120001_add_event_field_for_web_hook.rb
index 65f2a47bb3c451427dafeed6e5dcdc9303ccfeb2..89910893ee1b10e82acdea4f5613de58967ddabb 100644
--- a/db/migrate/20160227120001_add_event_field_for_web_hook.rb
+++ b/db/migrate/20160227120001_add_event_field_for_web_hook.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddEventFieldForWebHook < ActiveRecord::Migration
   def change
     add_column :web_hooks, :wiki_page_events, :boolean, default: false, null: false
diff --git a/db/migrate/20160227120047_add_event_to_services.rb b/db/migrate/20160227120047_add_event_to_services.rb
index f5040d770de905a04057b537ed75e29b83cfb7e1..fe7c54ca4eb915e92db1caa056d7eb1cc78920ef 100644
--- a/db/migrate/20160227120047_add_event_to_services.rb
+++ b/db/migrate/20160227120047_add_event_to_services.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddEventToServices < ActiveRecord::Migration
   def change
     add_column :services, :wiki_page_events, :boolean, default: true
diff --git a/db/migrate/20160229193553_add_main_language_to_repository.rb b/db/migrate/20160229193553_add_main_language_to_repository.rb
index b5446c6a4472c43f5eaf69a83487cc0a95d9edec..ad5167b4c939b80ae680c9bdd1e20ef59bf350cb 100644
--- a/db/migrate/20160229193553_add_main_language_to_repository.rb
+++ b/db/migrate/20160229193553_add_main_language_to_repository.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddMainLanguageToRepository < ActiveRecord::Migration
   def change
     add_column :projects, :main_language, :string
diff --git a/db/migrate/20160301124843_add_visibility_level_to_groups.rb b/db/migrate/20160301124843_add_visibility_level_to_groups.rb
index d1b921bb208e897aa23322e942d7fc3571433438..a874e6758dd232ca8dcc04c884925fe33610a9f2 100644
--- a/db/migrate/20160301124843_add_visibility_level_to_groups.rb
+++ b/db/migrate/20160301124843_add_visibility_level_to_groups.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddVisibilityLevelToGroups < ActiveRecord::Migration
   def up
     add_column :namespaces, :visibility_level, :integer, null: false, default: Gitlab::VisibilityLevel::PUBLIC
diff --git a/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb b/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb
index ffcd64266e3801b4676c67eab19ca98e3bf4cf05..1f400566f9f79379a25878821fd1b715a8d3cbb8 100644
--- a/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb
+++ b/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddImportCredentialsToProjectImportData < ActiveRecord::Migration
   def change
     add_column :project_import_data, :encrypted_credentials, :text
diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb
index 561c18a577616416931b1a73a8f407cb28681079..ac7eac0ea7c64589f0d6302eb263244567a86b97 100644
--- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb
+++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 # Loops through old importer projects that kept a token/password in the import URL
 # and encrypts the credentials into a separate field in project#import_data
 # #down method not supported
@@ -28,7 +29,7 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration
 
         update_import_url(import_url, project)
         update_import_data(import_url, project)
-      rescue URI::InvalidURIError
+      rescue Addressable::URI::InvalidURIError
         nullify_import_url(project)
       end
     end
diff --git a/db/migrate/20160305220806_remove_expires_at_from_snippets.rb b/db/migrate/20160305220806_remove_expires_at_from_snippets.rb
index fc12b5b09e69fb2e893941e4c14c122f9a7501bd..cac78703bc22bcb17e7c67e9a8a1350ff5e2fb8d 100644
--- a/db/migrate/20160305220806_remove_expires_at_from_snippets.rb
+++ b/db/migrate/20160305220806_remove_expires_at_from_snippets.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveExpiresAtFromSnippets < ActiveRecord::Migration
   def change
     remove_column :snippets, :expires_at, :datetime
diff --git a/db/migrate/20160307221555_disallow_blank_line_code_on_note.rb b/db/migrate/20160307221555_disallow_blank_line_code_on_note.rb
index 49e787d9a9a598ecb877d5e6e0fb97fadcc0c3e8..10f2b8cc56a8dcd8553f3f381767316bb95d90c7 100644
--- a/db/migrate/20160307221555_disallow_blank_line_code_on_note.rb
+++ b/db/migrate/20160307221555_disallow_blank_line_code_on_note.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class DisallowBlankLineCodeOnNote < ActiveRecord::Migration
   def up
     execute("UPDATE notes SET line_code = NULL WHERE line_code = ''")
diff --git a/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb b/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb
index 72b862d67d2fdaaf2b7d22411169d7b12443831b..92c0a1e088e9ac692de13b64fff77cff1b43b5cf 100644
--- a/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb
+++ b/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 # Create visibility level field on DB
 # Sets default_visibility_level to value on settings if not restricted
 # If value is restricted takes higher visibility level allowed
diff --git a/db/migrate/20160309140734_fix_todos.rb b/db/migrate/20160309140734_fix_todos.rb
index ebe0fc82305d87de35e0cc7bfabc1ba74f9695db..94fe1e4fdc300071bf95c5ed6723e0418e7c387e 100644
--- a/db/migrate/20160309140734_fix_todos.rb
+++ b/db/migrate/20160309140734_fix_todos.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class FixTodos < ActiveRecord::Migration
  def up
     execute <<-SQL
diff --git a/db/migrate/20160310124959_add_due_date_to_issues.rb b/db/migrate/20160310124959_add_due_date_to_issues.rb
index ec08bd9fdfacb293f6dce607538218bbd3273a66..a4eb6aaee63ed0e1256daba27defc532c449d158 100644
--- a/db/migrate/20160310124959_add_due_date_to_issues.rb
+++ b/db/migrate/20160310124959_add_due_date_to_issues.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddDueDateToIssues < ActiveRecord::Migration
   def change
     add_column :issues, :due_date, :date
diff --git a/db/migrate/20160310185910_add_external_flag_to_users.rb b/db/migrate/20160310185910_add_external_flag_to_users.rb
index 54937f1eb711f974da2a85f1ea064c46597f8c23..209496dc7861aa42e9a990b3aa7c89cb97dbc83b 100644
--- a/db/migrate/20160310185910_add_external_flag_to_users.rb
+++ b/db/migrate/20160310185910_add_external_flag_to_users.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddExternalFlagToUsers < ActiveRecord::Migration
   def change
     add_column :users, :external, :boolean, default: false
diff --git a/db/migrate/20160314094147_add_priority_to_label.rb b/db/migrate/20160314094147_add_priority_to_label.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7fb23cba4c90ee8c04b062d05a2475514761e7be
--- /dev/null
+++ b/db/migrate/20160314094147_add_priority_to_label.rb
@@ -0,0 +1,7 @@
+# rubocop:disable all
+class AddPriorityToLabel < ActiveRecord::Migration
+  def change
+    add_column :labels, :priority, :integer
+    add_index :labels, :priority
+  end
+end
diff --git a/db/migrate/20160314114439_add_requested_at_to_members.rb b/db/migrate/20160314114439_add_requested_at_to_members.rb
new file mode 100644
index 0000000000000000000000000000000000000000..273819d4cd8069f160d0a360bfa72f1610d16f03
--- /dev/null
+++ b/db/migrate/20160314114439_add_requested_at_to_members.rb
@@ -0,0 +1,5 @@
+class AddRequestedAtToMembers < ActiveRecord::Migration
+  def change
+    add_column :members, :requested_at, :datetime
+  end
+end
diff --git a/db/migrate/20160314143402_projects_add_pushes_since_gc.rb b/db/migrate/20160314143402_projects_add_pushes_since_gc.rb
index 5d30a38bc99bd5aaecda032b74ee9d8eab6434b0..9f8ffe073a316dbb5f771aa8dec7dffa7f707a08 100644
--- a/db/migrate/20160314143402_projects_add_pushes_since_gc.rb
+++ b/db/migrate/20160314143402_projects_add_pushes_since_gc.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class ProjectsAddPushesSinceGc < ActiveRecord::Migration
   def change
     add_column :projects, :pushes_since_gc, :integer, default: 0
diff --git a/db/migrate/20160315135439_project_add_repository_check.rb b/db/migrate/20160315135439_project_add_repository_check.rb
index 8687d5d62965bc8def4a7ab1874cbfb1ad35ae5f..8fe649246c7478f063a48565d9fb62dbae136000 100644
--- a/db/migrate/20160315135439_project_add_repository_check.rb
+++ b/db/migrate/20160315135439_project_add_repository_check.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class ProjectAddRepositoryCheck < ActiveRecord::Migration
   def change
     add_column :projects, :last_repository_check_failed, :boolean
diff --git a/db/migrate/20160316123110_ci_runners_token_index.rb b/db/migrate/20160316123110_ci_runners_token_index.rb
index 67bf5b4f97827560b2d4df1cf768d18c9a5eb009..ff3d36d68ee21caf4add6e88745d2a7e8c2cc395 100644
--- a/db/migrate/20160316123110_ci_runners_token_index.rb
+++ b/db/migrate/20160316123110_ci_runners_token_index.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CiRunnersTokenIndex < ActiveRecord::Migration
   disable_ddl_transaction!
 
diff --git a/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb b/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb
index 6871b3920df145b0d609cddb8b7ba8dc881f0851..65e0e61c78f6c8054b94e32855a88319bdfe8c43 100644
--- a/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb
+++ b/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class ChangeTargetIdToNullOnTodos < ActiveRecord::Migration
   def change
     change_column_null :todos, :target_id, true
diff --git a/db/migrate/20160316204731_add_commit_id_to_todos.rb b/db/migrate/20160316204731_add_commit_id_to_todos.rb
index ae19fdd1abd3c525a4955a527ec2b89215e285f3..d79858fc920d06ee14caef0f8be95e91e09533e2 100644
--- a/db/migrate/20160316204731_add_commit_id_to_todos.rb
+++ b/db/migrate/20160316204731_add_commit_id_to_todos.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddCommitIdToTodos < ActiveRecord::Migration
   def change
     add_column :todos, :commit_id, :string
diff --git a/db/migrate/20160317092222_add_moved_to_to_issue.rb b/db/migrate/20160317092222_add_moved_to_to_issue.rb
index 461e7fb3a9bdf3d0e30879290bd02d777bfc0067..9dde668ddff90338833c868b05217a03d10e3e69 100644
--- a/db/migrate/20160317092222_add_moved_to_to_issue.rb
+++ b/db/migrate/20160317092222_add_moved_to_to_issue.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddMovedToToIssue < ActiveRecord::Migration
   def change
     add_reference :issues, :moved_to, references: :issues
diff --git a/db/migrate/20160320204112_index_namespaces_on_visibility_level.rb b/db/migrate/20160320204112_index_namespaces_on_visibility_level.rb
index 370b339d45c7ace3c748cbb868e58e4c0a54d9f5..07ae7c95477d6cec99150d6ab374332188fc2ee4 100644
--- a/db/migrate/20160320204112_index_namespaces_on_visibility_level.rb
+++ b/db/migrate/20160320204112_index_namespaces_on_visibility_level.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class IndexNamespacesOnVisibilityLevel < ActiveRecord::Migration
   def change
     unless index_exists?(:namespaces, :visibility_level)
diff --git a/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb b/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb
index 1fff9759d1e387944baee32d957bb39fbbfb0dfa..a9a851cfe638ff95941ce57c97eaabff2159c25a 100644
--- a/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb
+++ b/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveTodosForDeletedIssues < ActiveRecord::Migration
   def up
     execute <<-SQL
diff --git a/db/migrate/20160328112808_create_notification_settings.rb b/db/migrate/20160328112808_create_notification_settings.rb
index 4755da8b8066a3d498651e617fd4301c4febcad5..7d77e8004baf04c26f8496b645a898f781fb517f 100644
--- a/db/migrate/20160328112808_create_notification_settings.rb
+++ b/db/migrate/20160328112808_create_notification_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class CreateNotificationSettings < ActiveRecord::Migration
   def change
     create_table :notification_settings do |t|
diff --git a/db/migrate/20160328115649_migrate_new_notification_setting.rb b/db/migrate/20160328115649_migrate_new_notification_setting.rb
index 3c81b2c37bfe558724b4663f3fdbdf585874cc3f..eb6b7d0721950f460432190b9d33ec242b7260b0 100644
--- a/db/migrate/20160328115649_migrate_new_notification_setting.rb
+++ b/db/migrate/20160328115649_migrate_new_notification_setting.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 # This migration will create one row of NotificationSetting for each Member row
 # It can take long time on big instances.
 #
diff --git a/db/migrate/20160328121138_add_notification_setting_index.rb b/db/migrate/20160328121138_add_notification_setting_index.rb
index 8aebce0244d68273de80ce36e6333b710628095f..667270d6b048880aec06738be8e4a350074e8a7b 100644
--- a/db/migrate/20160328121138_add_notification_setting_index.rb
+++ b/db/migrate/20160328121138_add_notification_setting_index.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddNotificationSettingIndex < ActiveRecord::Migration
   def change
     add_index :notification_settings, :user_id
diff --git a/db/migrate/20160329144452_add_index_on_pending_delete_projects.rb b/db/migrate/20160329144452_add_index_on_pending_delete_projects.rb
index 275554e736e55987ab882d26addb160671b3d4ed..a3df8fb4e2ec23a8499379382a14ce6f79a96468 100644
--- a/db/migrate/20160329144452_add_index_on_pending_delete_projects.rb
+++ b/db/migrate/20160329144452_add_index_on_pending_delete_projects.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddIndexOnPendingDeleteProjects < ActiveRecord::Migration
   def change
     add_index :projects, :pending_delete
diff --git a/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb b/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb
index 54cea964ff2c789786bab34bd2468397fab81c5a..b15af79b9b5bd498c58c0aca9bc548a6a0e13516 100644
--- a/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb
+++ b/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveTodosForDeletedMergeRequests < ActiveRecord::Migration
   def up
     execute <<-SQL
diff --git a/db/migrate/20160331223143_remove_twitter_sharing_enabled_from_application_settings.rb b/db/migrate/20160331223143_remove_twitter_sharing_enabled_from_application_settings.rb
index 0d736e323b6feddb45e95f646daa71c9f8f454a5..dec80497fb31db153cfcca4406ff23314b32eb69 100644
--- a/db/migrate/20160331223143_remove_twitter_sharing_enabled_from_application_settings.rb
+++ b/db/migrate/20160331223143_remove_twitter_sharing_enabled_from_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveTwitterSharingEnabledFromApplicationSettings < ActiveRecord::Migration
   def change
     remove_column :application_settings, :twitter_sharing_enabled, :boolean
diff --git a/db/migrate/20160407120251_add_images_enabled_for_project.rb b/db/migrate/20160407120251_add_images_enabled_for_project.rb
index 47f0ca8e8debdcff19b68783618e2fb3f5d17295..fcffc98b47ae70e660eb3f6624596923c96ae774 100644
--- a/db/migrate/20160407120251_add_images_enabled_for_project.rb
+++ b/db/migrate/20160407120251_add_images_enabled_for_project.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddImagesEnabledForProject < ActiveRecord::Migration
   def change
     add_column :projects, :container_registry_enabled, :boolean
diff --git a/db/migrate/20160412140240_add_repository_checks_enabled_setting.rb b/db/migrate/20160412140240_add_repository_checks_enabled_setting.rb
index ebfa4bcbc7b9dc91afd541303e476335094a4972..920d4d411104151f3ff58355a32897698e707f60 100644
--- a/db/migrate/20160412140240_add_repository_checks_enabled_setting.rb
+++ b/db/migrate/20160412140240_add_repository_checks_enabled_setting.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddRepositoryChecksEnabledSetting < ActiveRecord::Migration
   def change
     add_column :application_settings, :repository_checks_enabled, :boolean, default: true
diff --git a/db/migrate/20160412173416_add_fields_to_ci_commit.rb b/db/migrate/20160412173416_add_fields_to_ci_commit.rb
index 125956a3ddd9c784b99b93aaf6e7365b4247e68e..00162af5cdadd0eea8b1511df8f84c4b61765eb3 100644
--- a/db/migrate/20160412173416_add_fields_to_ci_commit.rb
+++ b/db/migrate/20160412173416_add_fields_to_ci_commit.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddFieldsToCiCommit < ActiveRecord::Migration
   def change
     add_column :ci_commits, :status, :string
diff --git a/db/migrate/20160412173417_update_ci_commit.rb b/db/migrate/20160412173417_update_ci_commit.rb
index fd92444dbacff1c96adfdf2d310c3b864a507bc3..858faeb060e68f6114f885afedbdf83cfcc88f6a 100644
--- a/db/migrate/20160412173417_update_ci_commit.rb
+++ b/db/migrate/20160412173417_update_ci_commit.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class UpdateCiCommit < ActiveRecord::Migration
   # This migration can be run online, but needs to be executed for the second time after restarting Unicorn workers
   # Otherwise Offline migration should be used.
diff --git a/db/migrate/20160412173418_add_ci_commit_indexes.rb b/db/migrate/20160412173418_add_ci_commit_indexes.rb
index 603d4a4161088ca631c7481aa053867cba40fba1..414f1f8279fe74aeb2bf97474141fb78b87937c8 100644
--- a/db/migrate/20160412173418_add_ci_commit_indexes.rb
+++ b/db/migrate/20160412173418_add_ci_commit_indexes.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddCiCommitIndexes < ActiveRecord::Migration
   disable_ddl_transaction!
 
diff --git a/db/migrate/20160413115152_add_token_to_web_hooks.rb b/db/migrate/20160413115152_add_token_to_web_hooks.rb
index f04225068cd2cf08c851b0d062d66b1f6450d19a..628b1d51b30a6f7eccf98c296233d65843e87369 100644
--- a/db/migrate/20160413115152_add_token_to_web_hooks.rb
+++ b/db/migrate/20160413115152_add_token_to_web_hooks.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddTokenToWebHooks < ActiveRecord::Migration
   def change
     add_column :web_hooks, :token, :string
diff --git a/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb b/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb
index d493044c67b83148e561688cd8c9716c257decb3..b53b9bc6c3d7584df4bf059190495a95f1b1d157 100644
--- a/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb
+++ b/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddSharedRunnersTextToApplicationSettings < ActiveRecord::Migration
   def change
     add_column :application_settings, :shared_runners_text, :text
diff --git a/db/migrate/20160416180807_add_award_emoji.rb b/db/migrate/20160416180807_add_award_emoji.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a3bee9b1bc69c2557a15e9beae5ed5e1f94864f1
--- /dev/null
+++ b/db/migrate/20160416180807_add_award_emoji.rb
@@ -0,0 +1,15 @@
+# rubocop:disable all
+class AddAwardEmoji < ActiveRecord::Migration
+  def change
+    create_table :award_emoji do |t|
+      t.string :name
+      t.references :user
+      t.references :awardable, polymorphic: true
+
+      t.timestamps
+    end
+
+    add_index :award_emoji, :user_id
+    add_index :award_emoji, [:awardable_type, :awardable_id]
+  end
+end
diff --git a/db/migrate/20160416182152_convert_award_note_to_emoji_award.rb b/db/migrate/20160416182152_convert_award_note_to_emoji_award.rb
new file mode 100644
index 0000000000000000000000000000000000000000..95ee03611d923b7b04e40f0df0248b5199a6de74
--- /dev/null
+++ b/db/migrate/20160416182152_convert_award_note_to_emoji_award.rb
@@ -0,0 +1,37 @@
+# rubocop:disable all
+class ConvertAwardNoteToEmojiAward < ActiveRecord::Migration
+  disable_ddl_transaction!
+
+  def up
+    if Gitlab::Database.postgresql?
+      migrate_postgresql
+    else
+      migrate_mysql
+    end
+  end
+
+  def down
+    add_column :notes, :is_award, :boolean
+
+    # This migration does NOT move the awards on notes, if the table is dropped in another migration, these notes will be lost.
+    execute "INSERT INTO notes (noteable_type, noteable_id, author_id, note, created_at, updated_at, is_award) (SELECT awardable_type, awardable_id, user_id, name, created_at, updated_at, TRUE FROM award_emoji)"
+  end
+
+  def migrate_postgresql
+    connection.transaction do
+      execute 'LOCK notes IN EXCLUSIVE MODE'
+      execute "INSERT INTO award_emoji (awardable_type, awardable_id, user_id, name, created_at, updated_at) (SELECT noteable_type, noteable_id, author_id, note, created_at, updated_at FROM notes WHERE is_award = true)"
+      execute "DELETE FROM notes WHERE is_award = true"
+      remove_column :notes, :is_award, :boolean
+    end
+  end
+
+  def migrate_mysql
+    execute 'LOCK TABLES notes WRITE, award_emoji WRITE;'
+    execute 'INSERT INTO award_emoji (awardable_type, awardable_id, user_id, name, created_at, updated_at) (SELECT noteable_type, noteable_id, author_id, note, created_at, updated_at FROM notes WHERE is_award = true);'
+    execute "DELETE FROM notes WHERE is_award = true"
+    remove_column :notes, :is_award, :boolean
+  ensure
+    execute 'UNLOCK TABLES'
+  end
+end
diff --git a/db/migrate/20160419120017_add_metrics_packet_size.rb b/db/migrate/20160419120017_add_metrics_packet_size.rb
index 78c163d62acf8782c3d7294b6858e174d5921392..c759427c59031c7c2df8bd99886db56fb2ea4e18 100644
--- a/db/migrate/20160419120017_add_metrics_packet_size.rb
+++ b/db/migrate/20160419120017_add_metrics_packet_size.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddMetricsPacketSize < ActiveRecord::Migration
   def change
     add_column :application_settings, :metrics_packet_size, :integer, default: 1
diff --git a/db/migrate/20160419122101_add_only_allow_merge_if_build_succeeds_to_projects.rb b/db/migrate/20160419122101_add_only_allow_merge_if_build_succeeds_to_projects.rb
new file mode 100644
index 0000000000000000000000000000000000000000..69d64ccd00617237c0e511f628e261ea35ddb0f0
--- /dev/null
+++ b/db/migrate/20160419122101_add_only_allow_merge_if_build_succeeds_to_projects.rb
@@ -0,0 +1,15 @@
+class AddOnlyAllowMergeIfBuildSucceedsToProjects < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+  disable_ddl_transaction!
+
+  def up
+    add_column_with_default(:projects,
+                            :only_allow_merge_if_build_succeeds,
+                            :boolean,
+                            default: false)
+  end
+
+  def down
+    remove_column(:projects, :only_allow_merge_if_build_succeeds)
+  end
+end
diff --git a/db/migrate/20160421130527_disable_repository_checks.rb b/db/migrate/20160421130527_disable_repository_checks.rb
index 808a4b93c7c596adf0bd8b84c8752f268d36d7b9..7e65ddc45e73186c605ba91c4cf17ba40a8072fc 100644
--- a/db/migrate/20160421130527_disable_repository_checks.rb
+++ b/db/migrate/20160421130527_disable_repository_checks.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class DisableRepositoryChecks < ActiveRecord::Migration
   def up
     change_column_default :application_settings, :repository_checks_enabled, false 
diff --git a/db/migrate/20160425045124_create_u2f_registrations.rb b/db/migrate/20160425045124_create_u2f_registrations.rb
new file mode 100644
index 0000000000000000000000000000000000000000..72cbe98ebba3615766cd9b4f45b30e6a6fdfe6cc
--- /dev/null
+++ b/db/migrate/20160425045124_create_u2f_registrations.rb
@@ -0,0 +1,14 @@
+# rubocop:disable all
+class CreateU2fRegistrations < ActiveRecord::Migration
+  def change
+    create_table :u2f_registrations do |t|
+      t.text :certificate
+      t.string :key_handle, index: true
+      t.string :public_key
+      t.integer :counter
+      t.references :user, index: true, foreign_key: true
+
+      t.timestamps null: false
+    end
+  end
+end
diff --git a/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb b/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb
index facd33875ba1efcf2d6571935d09a0bfdf2548a2..bf50616656c3c4bc181f18fff3bb30a8094d6075 100644
--- a/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb
+++ b/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddDisabledOauthSignInSourcesToApplicationSettings < ActiveRecord::Migration
   def change
     add_column :application_settings, :disabled_oauth_sign_in_sources, :text
diff --git a/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb b/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb
index 84e5e4eabe27657474fa30cd834139be89e519e0..c60892a6279cc3c0f40da7f912f052eb13b36664 100644
--- a/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb
+++ b/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddRunUntaggedToCiRunner < ActiveRecord::Migration
   include Gitlab::Database::MigrationHelpers
   disable_ddl_transaction!
diff --git a/db/migrate/20160508194200_remove_wall_enabled_from_projects.rb b/db/migrate/20160508194200_remove_wall_enabled_from_projects.rb
index aa560bc0f0cbb8b59c8dd03b0fbc9a3a1c8caf01..6792ffc957abd394906742223e7bd612590b1690 100644
--- a/db/migrate/20160508194200_remove_wall_enabled_from_projects.rb
+++ b/db/migrate/20160508194200_remove_wall_enabled_from_projects.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class RemoveWallEnabledFromProjects < ActiveRecord::Migration
   def change
     remove_column :projects, :wall_enabled, :boolean, default: true, null: false
diff --git a/db/migrate/20160508215820_add_type_to_notes.rb b/db/migrate/20160508215820_add_type_to_notes.rb
index 58944d4e651c4e44328deb6dfc317cac5f8724b2..c1d07c9363fdbda9d929ead3588a5b0c796e97fb 100644
--- a/db/migrate/20160508215820_add_type_to_notes.rb
+++ b/db/migrate/20160508215820_add_type_to_notes.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddTypeToNotes < ActiveRecord::Migration
   def change
     add_column :notes, :type, :string
diff --git a/db/migrate/20160508221410_set_type_on_legacy_diff_notes.rb b/db/migrate/20160508221410_set_type_on_legacy_diff_notes.rb
index c3f23d89d5a127a48363479d1e9c35175f726b2f..6dd958ff4a07415edefd84d823b5341143d19809 100644
--- a/db/migrate/20160508221410_set_type_on_legacy_diff_notes.rb
+++ b/db/migrate/20160508221410_set_type_on_legacy_diff_notes.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class SetTypeOnLegacyDiffNotes < ActiveRecord::Migration
   def change
     execute "UPDATE notes SET type = 'LegacyDiffNote' WHERE line_code IS NOT NULL"
diff --git a/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb b/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb
index 9d729fec189874fac8611917fa2a080501397571..b6a5bea79b65b3595949746060f452c1d5ab5c03 100644
--- a/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb
+++ b/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddHealthCheckAccessTokenToApplicationSettings < ActiveRecord::Migration
   def change
     add_column :application_settings, :health_check_access_token, :string
diff --git a/db/migrate/20160516174813_add_send_user_confirmation_email_to_application_settings.rb b/db/migrate/20160516174813_add_send_user_confirmation_email_to_application_settings.rb
index c34e7ba540951aabcc12826936501d8b4c4b559d..8c96353b850e05c7819466004420511c2a2b2344 100644
--- a/db/migrate/20160516174813_add_send_user_confirmation_email_to_application_settings.rb
+++ b/db/migrate/20160516174813_add_send_user_confirmation_email_to_application_settings.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class AddSendUserConfirmationEmailToApplicationSettings < ActiveRecord::Migration
   def up
     add_column :application_settings, :send_user_confirmation_email, :boolean, default: false
diff --git a/db/migrate/20160518200441_add_artifacts_expire_date_to_ci_builds.rb b/db/migrate/20160518200441_add_artifacts_expire_date_to_ci_builds.rb
new file mode 100644
index 0000000000000000000000000000000000000000..915167b038d482490b3c394293736e53ed321ca6
--- /dev/null
+++ b/db/migrate/20160518200441_add_artifacts_expire_date_to_ci_builds.rb
@@ -0,0 +1,5 @@
+class AddArtifactsExpireDateToCiBuilds < ActiveRecord::Migration
+  def change
+    add_column :ci_builds, :artifacts_expire_at, :timestamp
+  end
+end
diff --git a/db/migrate/20160525205328_remove_main_language_from_projects.rb b/db/migrate/20160525205328_remove_main_language_from_projects.rb
new file mode 100644
index 0000000000000000000000000000000000000000..dc4ceacddb1b3cefd11dc2d46d29d7ffc66b0e92
--- /dev/null
+++ b/db/migrate/20160525205328_remove_main_language_from_projects.rb
@@ -0,0 +1,22 @@
+# rubocop:disable all
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class RemoveMainLanguageFromProjects < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  # When using the methods "add_concurrent_index" or "add_column_with_default"
+  # you must disable the use of transactions as these methods can not run in an
+  # existing transaction. When using "add_concurrent_index" make sure that this
+  # method is the _only_ method called in the migration, any other changes
+  # should go in a separate migration. This ensures that upon failure _only_ the
+  # index creation fails and can be retried or reverted easily.
+  #
+  # To disable transactions uncomment the following line and remove these
+  # comments:
+  # disable_ddl_transaction!
+
+  def change
+    remove_column :projects, :main_language
+  end
+end
diff --git a/db/migrate/20160527020117_remove_notification_settings_for_deleted_projects.rb b/db/migrate/20160527020117_remove_notification_settings_for_deleted_projects.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3e26be7c09c0b6f1db90535e4845caac06286c60
--- /dev/null
+++ b/db/migrate/20160527020117_remove_notification_settings_for_deleted_projects.rb
@@ -0,0 +1,14 @@
+# rubocop:disable all
+class RemoveNotificationSettingsForDeletedProjects < ActiveRecord::Migration
+  def up
+    execute <<-SQL
+      DELETE FROM notification_settings
+      WHERE notification_settings.source_type = 'Project'
+        AND NOT EXISTS (
+              SELECT *
+              FROM projects
+              WHERE projects.id = notification_settings.source_id
+            )
+    SQL
+  end
+end
diff --git a/db/migrate/20160528043124_add_users_state_index.rb b/db/migrate/20160528043124_add_users_state_index.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6419d2ae71d820eb98f9a412b66759cd56f685da
--- /dev/null
+++ b/db/migrate/20160528043124_add_users_state_index.rb
@@ -0,0 +1,10 @@
+# rubocop:disable all
+class AddUsersStateIndex < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  disable_ddl_transaction!
+
+  def change
+    add_concurrent_index :users, :state
+  end
+end
diff --git a/db/migrate/20160530150109_add_container_registry_token_expire_delay_to_application_settings.rb b/db/migrate/20160530150109_add_container_registry_token_expire_delay_to_application_settings.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d811fd5271e27784095f8c91234fc0cf6c4e922a
--- /dev/null
+++ b/db/migrate/20160530150109_add_container_registry_token_expire_delay_to_application_settings.rb
@@ -0,0 +1,10 @@
+# rubocop:disable all
+# This is ONLINE migration
+
+class AddContainerRegistryTokenExpireDelayToApplicationSettings < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  def change
+    add_column :application_settings, :container_registry_token_expire_delay, :integer, default: 5
+  end
+end
diff --git a/db/migrate/20160530214349_make_remote_mirrors_disabled_by_default.rb b/db/migrate/20160530214349_make_remote_mirrors_disabled_by_default.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5c29b7b4551513746e947c32fcfcb2db74dd9110
--- /dev/null
+++ b/db/migrate/20160530214349_make_remote_mirrors_disabled_by_default.rb
@@ -0,0 +1,25 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class MakeRemoteMirrorsDisabledByDefault < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  # When using the methods "add_concurrent_index" or "add_column_with_default"
+  # you must disable the use of transactions as these methods can not run in an
+  # existing transaction. When using "add_concurrent_index" make sure that this
+  # method is the _only_ method called in the migration, any other changes
+  # should go in a separate migration. This ensures that upon failure _only_ the
+  # index creation fails and can be retried or reverted easily.
+  #
+  # To disable transactions uncomment the following line and remove these
+  # comments:
+  # disable_ddl_transaction!
+
+  def up
+    change_column :remote_mirrors, :enabled, :boolean, default: false
+  end
+
+  def down
+    change_column :remote_mirrors, :enabled, :boolean, default: true
+  end
+end
diff --git a/db/migrate/20160601102211_create_path_locks_table.rb b/db/migrate/20160601102211_create_path_locks_table.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b147dcb1b82a8593983567347337c2ec7d4d0fb7
--- /dev/null
+++ b/db/migrate/20160601102211_create_path_locks_table.rb
@@ -0,0 +1,11 @@
+class CreatePathLocksTable < ActiveRecord::Migration
+  def change
+    create_table :path_locks do |t|
+      t.string :path, null: false, index: true
+      t.references :project, index: true, foreign_key: true
+      t.references :user, index: true, foreign_key: true
+
+      t.timestamps null: false
+    end
+  end
+end
diff --git a/db/migrate/20160603075128_add_has_external_issue_tracker_to_projects.rb b/db/migrate/20160603075128_add_has_external_issue_tracker_to_projects.rb
new file mode 100644
index 0000000000000000000000000000000000000000..be295f0181d34be79267748d10d62a6833e088ed
--- /dev/null
+++ b/db/migrate/20160603075128_add_has_external_issue_tracker_to_projects.rb
@@ -0,0 +1,10 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddHasExternalIssueTrackerToProjects < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  def change
+    add_column(:projects, :has_external_issue_tracker, :boolean)
+  end
+end
diff --git a/db/migrate/20160603180330_remove_duplicated_notification_settings.rb b/db/migrate/20160603180330_remove_duplicated_notification_settings.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4f4f58b1619473c47a02280d102df86c11ead5bb
--- /dev/null
+++ b/db/migrate/20160603180330_remove_duplicated_notification_settings.rb
@@ -0,0 +1,33 @@
+# rubocop:disable all
+class RemoveDuplicatedNotificationSettings < ActiveRecord::Migration
+  def up
+    duplicates = exec_query(%Q{
+      SELECT user_id, source_type, source_id
+      FROM notification_settings
+      GROUP BY user_id, source_type, source_id
+      HAVING COUNT(*) > 1
+    })
+
+    duplicates.each do |row|
+      uid = row['user_id']
+      stype = connection.quote(row['source_type'])
+      sid = row['source_id']
+
+      execute(%Q{
+        DELETE FROM notification_settings
+        WHERE user_id = #{uid}
+        AND source_type = #{stype}
+        AND source_id = #{sid}
+        AND id != (
+          SELECT id FROM (
+            SELECT min(id) AS id
+            FROM notification_settings
+            WHERE user_id = #{uid}
+            AND source_type = #{stype}
+            AND source_id = #{sid}
+          ) min_ids
+        )
+      })
+    end
+  end
+end
diff --git a/db/migrate/20160603182247_add_index_to_notification_settings.rb b/db/migrate/20160603182247_add_index_to_notification_settings.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f6ae26d555f446d6d8ad56dd43097f08f9654a6b
--- /dev/null
+++ b/db/migrate/20160603182247_add_index_to_notification_settings.rb
@@ -0,0 +1,10 @@
+# rubocop:disable all
+class AddIndexToNotificationSettings < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  disable_ddl_transaction!
+
+  def change
+    add_concurrent_index :notification_settings, [:user_id, :source_id, :source_type], { unique: true, name: "index_notifications_on_user_id_and_source_id_and_source_type" }
+  end
+end
diff --git a/db/migrate/20160608155312_add_after_sign_up_text_to_application_settings.rb b/db/migrate/20160608155312_add_after_sign_up_text_to_application_settings.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3c5d2ad910e37471d524ed87da3c89c78a4fdc15
--- /dev/null
+++ b/db/migrate/20160608155312_add_after_sign_up_text_to_application_settings.rb
@@ -0,0 +1,6 @@
+# rubocop:disable all
+class AddAfterSignUpTextToApplicationSettings < ActiveRecord::Migration
+  def change
+    add_column :application_settings, :after_sign_up_text, :text
+  end
+end
diff --git a/db/migrate/20160610140403_remove_notification_setting_not_null_constraints.rb b/db/migrate/20160610140403_remove_notification_setting_not_null_constraints.rb
new file mode 100644
index 0000000000000000000000000000000000000000..259abb08e4742096d5891923692dda1fef5fbc3c
--- /dev/null
+++ b/db/migrate/20160610140403_remove_notification_setting_not_null_constraints.rb
@@ -0,0 +1,11 @@
+class RemoveNotificationSettingNotNullConstraints < ActiveRecord::Migration
+  def up
+    change_column :notification_settings, :source_type, :string, null: true
+    change_column :notification_settings, :source_id, :integer, null: true
+  end
+
+  def down
+    change_column :notification_settings, :source_type, :string, null: false
+    change_column :notification_settings, :source_id, :integer, null: false
+  end
+end
diff --git a/db/migrate/20160610194713_remove_deprecated_issues_tracker_columns_from_projects.rb b/db/migrate/20160610194713_remove_deprecated_issues_tracker_columns_from_projects.rb
new file mode 100644
index 0000000000000000000000000000000000000000..477b2106dead5e5d23b06f0c5bafb93d26476733
--- /dev/null
+++ b/db/migrate/20160610194713_remove_deprecated_issues_tracker_columns_from_projects.rb
@@ -0,0 +1,6 @@
+class RemoveDeprecatedIssuesTrackerColumnsFromProjects < ActiveRecord::Migration
+  def change
+    remove_column :projects, :issues_tracker, :string, default: 'gitlab', null: false
+    remove_column :projects, :issues_tracker_id, :string
+  end
+end
diff --git a/db/migrate/20160610201627_migrate_users_notification_level.rb b/db/migrate/20160610201627_migrate_users_notification_level.rb
new file mode 100644
index 0000000000000000000000000000000000000000..760b766828e90420ccf6c38cbe5774bd115af5ac
--- /dev/null
+++ b/db/migrate/20160610201627_migrate_users_notification_level.rb
@@ -0,0 +1,21 @@
+class MigrateUsersNotificationLevel < ActiveRecord::Migration
+  # Migrates only users who changed their default notification level :participating
+  # creating a new record on notification settings table
+
+  def up
+    execute(%Q{
+      INSERT INTO notification_settings
+      (user_id, level, created_at, updated_at)
+      (SELECT id, notification_level, created_at, updated_at FROM users WHERE notification_level != 1)
+    })
+  end
+
+  # Migrates from notification settings back to user notification_level
+  # If no value is found the default level of 1 will be used
+  def down
+    execute(%Q{
+      UPDATE users u SET
+      notification_level = COALESCE((SELECT level FROM notification_settings WHERE user_id = u.id AND source_type IS NULL), 1)
+    })
+  end
+end
diff --git a/db/migrate/20160610204157_add_deployments.rb b/db/migrate/20160610204157_add_deployments.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cb144ea8a6d82c7cd8a005e8f0ee433100787bec
--- /dev/null
+++ b/db/migrate/20160610204157_add_deployments.rb
@@ -0,0 +1,27 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddDeployments < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  def change
+    create_table :deployments, force: true do |t|
+      t.integer  :iid,            null: false
+      t.integer  :project_id,     null: false
+      t.integer  :environment_id, null: false
+      t.string   :ref,            null: false
+      t.boolean  :tag,            null: false
+      t.string   :sha,            null: false
+      t.integer  :user_id
+      t.integer  :deployable_id
+      t.string   :deployable_type
+      t.datetime :created_at
+      t.datetime :updated_at
+    end
+
+    add_index :deployments, :project_id
+    add_index :deployments, [:project_id, :iid], unique: true
+    add_index :deployments, [:project_id, :environment_id]
+    add_index :deployments, [:project_id, :environment_id, :iid]
+  end
+end
diff --git a/db/migrate/20160610204158_add_environments.rb b/db/migrate/20160610204158_add_environments.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e1c71d173c41758e32e7c945c4e0e094a6a13ee8
--- /dev/null
+++ b/db/migrate/20160610204158_add_environments.rb
@@ -0,0 +1,17 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddEnvironments < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  def change
+    create_table :environments, force: true do |t|
+      t.integer  :project_id, null: false
+      t.string   :name,       null: false
+      t.datetime :created_at
+      t.datetime :updated_at
+    end
+
+    add_index :environments, [:project_id, :name]
+  end
+end
diff --git a/db/migrate/20160610211845_add_environment_to_builds.rb b/db/migrate/20160610211845_add_environment_to_builds.rb
new file mode 100644
index 0000000000000000000000000000000000000000..990e445ac55b07b5f60a0382fe32e6f3050b3514
--- /dev/null
+++ b/db/migrate/20160610211845_add_environment_to_builds.rb
@@ -0,0 +1,10 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddEnvironmentToBuilds < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  def change
+    add_column :ci_builds, :environment, :string
+  end
+end
diff --git a/db/migrate/20160610301627_remove_notification_level_from_users.rb b/db/migrate/20160610301627_remove_notification_level_from_users.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8afb14df2cf6b88232296d9ddcb23698f3d5b45a
--- /dev/null
+++ b/db/migrate/20160610301627_remove_notification_level_from_users.rb
@@ -0,0 +1,7 @@
+class RemoveNotificationLevelFromUsers < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  def change
+    remove_column :users, :notification_level, :integer
+  end
+end
diff --git a/db/migrate/20160611101122_add_es_to_application_settings.rb b/db/migrate/20160611101122_add_es_to_application_settings.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c8971fefbfa95101e830f86adc2e5f1a1206ccde
--- /dev/null
+++ b/db/migrate/20160611101122_add_es_to_application_settings.rb
@@ -0,0 +1,31 @@
+class AddEsToApplicationSettings < ActiveRecord::Migration
+  def up
+    add_column :application_settings, :elasticsearch_indexing, :boolean, default: false, null: false
+    add_column :application_settings, :elasticsearch_search, :boolean, default: false, null: false
+    add_column :application_settings, :elasticsearch_host, :string, default: 'localhost'
+    add_column :application_settings, :elasticsearch_port, :string, default: '9200'
+
+    es_enabled = Settings.elasticsearch['enabled']
+
+    es_host = Settings.elasticsearch['host']
+    es_host = es_host.join(',') if es_host.is_a?(Array)
+
+    es_port = Settings.elasticsearch['port']
+
+    execute <<-SQL.strip_heredoc
+      UPDATE application_settings
+      SET
+        elasticsearch_indexing = #{es_enabled},
+        elasticsearch_search = #{es_enabled},
+        elasticsearch_host = '#{es_host}',
+        elasticsearch_port = '#{es_port}'
+    SQL
+  end
+
+  def down
+    remove_column :application_settings, :elasticsearch_indexing
+    remove_column :application_settings, :elasticsearch_search
+    remove_column :application_settings, :elasticsearch_host
+    remove_column :application_settings, :elasticsearch_port
+  end
+end
diff --git a/db/migrate/20160615092001_disable_mirror_without_import_url.rb b/db/migrate/20160615092001_disable_mirror_without_import_url.rb
new file mode 100644
index 0000000000000000000000000000000000000000..502c167a6041370de453215a6235598ba5e0df9e
--- /dev/null
+++ b/db/migrate/20160615092001_disable_mirror_without_import_url.rb
@@ -0,0 +1,10 @@
+# RemoveWrongImportUrlFromProjects migration missed setting the mirror flag to false when making import_url nil
+# for invalid URIs that why we need this migration.
+
+class DisableMirrorWithoutImportUrl < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  def up
+    execute("UPDATE projects SET mirror = false WHERE projects.mirror = true AND (projects.import_url IS NULL OR projects.import_url = '')")
+  end
+end
diff --git a/db/migrate/20160615142710_add_index_on_requested_at_to_members.rb b/db/migrate/20160615142710_add_index_on_requested_at_to_members.rb
new file mode 100644
index 0000000000000000000000000000000000000000..63f7392e54fc073445b86680931dc61c21e1fa39
--- /dev/null
+++ b/db/migrate/20160615142710_add_index_on_requested_at_to_members.rb
@@ -0,0 +1,9 @@
+class AddIndexOnRequestedAtToMembers < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  disable_ddl_transaction!
+
+  def change
+    add_concurrent_index :members, :requested_at
+  end
+end
diff --git a/db/migrate/20160616084004_change_project_of_environment.rb b/db/migrate/20160616084004_change_project_of_environment.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cc1daf9b621c1c75350a4bd038aaf14f24c1bfe6
--- /dev/null
+++ b/db/migrate/20160616084004_change_project_of_environment.rb
@@ -0,0 +1,21 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class ChangeProjectOfEnvironment < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  # When using the methods "add_concurrent_index" or "add_column_with_default"
+  # you must disable the use of transactions as these methods can not run in an
+  # existing transaction. When using "add_concurrent_index" make sure that this
+  # method is the _only_ method called in the migration, any other changes
+  # should go in a separate migration. This ensures that upon failure _only_ the
+  # index creation fails and can be retried or reverted easily.
+  #
+  # To disable transactions uncomment the following line and remove these
+  # comments:
+  # disable_ddl_transaction!
+
+  def change
+    change_column_null :environments, :project_id, true
+  end
+end
diff --git a/db/migrate/limits_to_mysql.rb b/db/migrate/limits_to_mysql.rb
index 14d7e84d856c5b2fe634d81566681e4ff08c69a1..be3501c4c2e2035fcb1b899368ee188cd80c0675 100644
--- a/db/migrate/limits_to_mysql.rb
+++ b/db/migrate/limits_to_mysql.rb
@@ -1,3 +1,4 @@
+# rubocop:disable all
 class LimitsToMysql < ActiveRecord::Migration
   def up
     return unless ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/
diff --git a/db/schema.rb b/db/schema.rb
index a98a1c7e062cc78aef6e37f6ea7274822e1cb3ce..30ce7095ffbb1ea73b5ed864831a767ca4b9a055 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: 20160509201028) do
+ActiveRecord::Schema.define(version: 20160616084004) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -44,47 +44,54 @@ ActiveRecord::Schema.define(version: 20160509201028) do
     t.datetime "created_at"
     t.datetime "updated_at"
     t.string   "home_page_url"
-    t.integer  "default_branch_protection",         default: 2
+    t.integer  "default_branch_protection",             default: 2
     t.text     "help_text"
     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.boolean  "user_oauth_applications",               default: true
     t.string   "after_sign_out_path"
-    t.integer  "session_expire_delay",              default: 10080,       null: false
+    t.integer  "session_expire_delay",                  default: 10080,       null: false
     t.text     "import_sources"
     t.text     "help_page_text"
     t.string   "admin_notification_email"
-    t.boolean  "shared_runners_enabled",            default: true,        null: false
-    t.integer  "max_artifacts_size",                default: 100,         null: false
+    t.boolean  "shared_runners_enabled",                default: true,        null: false
+    t.integer  "max_artifacts_size",                    default: 100,         null: false
     t.string   "runners_registration_token"
-    t.integer  "max_pages_size",                    default: 100,         null: false
-    t.boolean  "require_two_factor_authentication", default: false
-    t.integer  "two_factor_grace_period",           default: 48
-    t.boolean  "metrics_enabled",                   default: false
-    t.string   "metrics_host",                      default: "localhost"
-    t.integer  "metrics_pool_size",                 default: 16
-    t.integer  "metrics_timeout",                   default: 10
-    t.integer  "metrics_method_call_threshold",     default: 10
-    t.boolean  "recaptcha_enabled",                 default: false
+    t.integer  "max_pages_size",                        default: 100,         null: false
+    t.boolean  "require_two_factor_authentication",     default: false
+    t.integer  "two_factor_grace_period",               default: 48
+    t.boolean  "metrics_enabled",                       default: false
+    t.string   "metrics_host",                          default: "localhost"
+    t.integer  "metrics_pool_size",                     default: 16
+    t.integer  "metrics_timeout",                       default: 10
+    t.integer  "metrics_method_call_threshold",         default: 10
+    t.boolean  "recaptcha_enabled",                     default: false
     t.string   "recaptcha_site_key"
     t.string   "recaptcha_private_key"
-    t.integer  "metrics_port",                      default: 8089
-    t.boolean  "akismet_enabled",                   default: false
+    t.integer  "metrics_port",                          default: 8089
+    t.boolean  "akismet_enabled",                       default: false
     t.string   "akismet_api_key"
-    t.integer  "metrics_sample_interval",           default: 15
-    t.boolean  "sentry_enabled",                    default: false
+    t.integer  "metrics_sample_interval",               default: 15
+    t.boolean  "sentry_enabled",                        default: false
     t.string   "sentry_dsn"
-    t.boolean  "email_author_in_body",              default: false
+    t.boolean  "email_author_in_body",                  default: false
     t.integer  "default_group_visibility"
-    t.boolean  "repository_checks_enabled",         default: false
+    t.boolean  "repository_checks_enabled",             default: false
     t.text     "shared_runners_text"
-    t.integer  "metrics_packet_size",               default: 1
+    t.integer  "metrics_packet_size",                   default: 1
     t.text     "disabled_oauth_sign_in_sources"
     t.string   "health_check_access_token"
+    t.boolean  "send_user_confirmation_email",          default: false
+    t.integer  "container_registry_token_expire_delay", default: 5
+    t.text     "after_sign_up_text"
+    t.boolean  "elasticsearch_indexing",                default: false,       null: false
+    t.boolean  "elasticsearch_search",                  default: false,       null: false
+    t.string   "elasticsearch_host",                    default: "localhost"
+    t.string   "elasticsearch_port",                    default: "9200"
   end
 
   create_table "approvals", force: :cascade do |t|
@@ -119,6 +126,18 @@ ActiveRecord::Schema.define(version: 20160509201028) do
   add_index "audit_events", ["entity_id", "entity_type"], name: "index_audit_events_on_entity_id_and_entity_type", using: :btree
   add_index "audit_events", ["type"], name: "index_audit_events_on_type", using: :btree
 
+  create_table "award_emoji", force: :cascade do |t|
+    t.string   "name"
+    t.integer  "user_id"
+    t.integer  "awardable_id"
+    t.string   "awardable_type"
+    t.datetime "created_at"
+    t.datetime "updated_at"
+  end
+
+  add_index "award_emoji", ["awardable_type", "awardable_id"], name: "index_award_emoji_on_awardable_type_and_awardable_id", using: :btree
+  add_index "award_emoji", ["user_id"], name: "index_award_emoji_on_user_id", using: :btree
+
   create_table "broadcast_messages", force: :cascade do |t|
     t.text     "message",    null: false
     t.datetime "starts_at"
@@ -150,9 +169,9 @@ ActiveRecord::Schema.define(version: 20160509201028) do
     t.text     "commands"
     t.integer  "job_id"
     t.string   "name"
-    t.boolean  "deploy",             default: false
+    t.boolean  "deploy",              default: false
     t.text     "options"
-    t.boolean  "allow_failure",      default: false, null: false
+    t.boolean  "allow_failure",       default: false, null: false
     t.string   "stage"
     t.integer  "trigger_request_id"
     t.integer  "stage_idx"
@@ -167,6 +186,8 @@ ActiveRecord::Schema.define(version: 20160509201028) do
     t.text     "artifacts_metadata"
     t.integer  "erased_by_id"
     t.datetime "erased_at"
+    t.datetime "artifacts_expire_at"
+    t.string   "environment"
   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
@@ -387,6 +408,25 @@ ActiveRecord::Schema.define(version: 20160509201028) do
 
   add_index "deploy_keys_projects", ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree
 
+  create_table "deployments", force: :cascade do |t|
+    t.integer  "iid",             null: false
+    t.integer  "project_id",      null: false
+    t.integer  "environment_id",  null: false
+    t.string   "ref",             null: false
+    t.boolean  "tag",             null: false
+    t.string   "sha",             null: false
+    t.integer  "user_id"
+    t.integer  "deployable_id"
+    t.string   "deployable_type"
+    t.datetime "created_at"
+    t.datetime "updated_at"
+  end
+
+  add_index "deployments", ["project_id", "environment_id", "iid"], name: "index_deployments_on_project_id_and_environment_id_and_iid", using: :btree
+  add_index "deployments", ["project_id", "environment_id"], name: "index_deployments_on_project_id_and_environment_id", using: :btree
+  add_index "deployments", ["project_id", "iid"], name: "index_deployments_on_project_id_and_iid", unique: true, using: :btree
+  add_index "deployments", ["project_id"], name: "index_deployments_on_project_id", using: :btree
+
   create_table "emails", force: :cascade do |t|
     t.integer  "user_id",    null: false
     t.string   "email",      null: false
@@ -397,6 +437,15 @@ ActiveRecord::Schema.define(version: 20160509201028) do
   add_index "emails", ["email"], name: "index_emails_on_email", unique: true, using: :btree
   add_index "emails", ["user_id"], name: "index_emails_on_user_id", using: :btree
 
+  create_table "environments", force: :cascade do |t|
+    t.integer  "project_id"
+    t.string   "name",       null: false
+    t.datetime "created_at"
+    t.datetime "updated_at"
+  end
+
+  add_index "environments", ["project_id", "name"], name: "index_environments_on_project_id_and_name", using: :btree
+
   create_table "events", force: :cascade do |t|
     t.string   "target_type"
     t.integer  "target_id"
@@ -557,8 +606,10 @@ ActiveRecord::Schema.define(version: 20160509201028) do
     t.datetime "updated_at"
     t.boolean  "template",    default: false
     t.string   "description"
+    t.integer  "priority"
   end
 
+  add_index "labels", ["priority"], name: "index_labels_on_priority", using: :btree
   add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree
 
   create_table "ldap_group_links", force: :cascade do |t|
@@ -608,11 +659,13 @@ ActiveRecord::Schema.define(version: 20160509201028) do
     t.string   "invite_email"
     t.string   "invite_token"
     t.datetime "invite_accepted_at"
+    t.datetime "requested_at"
   end
 
   add_index "members", ["access_level"], name: "index_members_on_access_level", using: :btree
   add_index "members", ["created_at", "id"], name: "index_members_on_created_at_and_id", using: :btree
   add_index "members", ["invite_token"], name: "index_members_on_invite_token", unique: true, using: :btree
+  add_index "members", ["requested_at"], name: "index_members_on_requested_at", using: :btree
   add_index "members", ["source_id", "source_type"], name: "index_members_on_source_id_and_source_type", using: :btree
   add_index "members", ["type"], name: "index_members_on_type", using: :btree
   add_index "members", ["user_id"], name: "index_members_on_user_id", using: :btree
@@ -725,10 +778,9 @@ ActiveRecord::Schema.define(version: 20160509201028) do
     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.string   "type"
   end
 
@@ -736,7 +788,6 @@ ActiveRecord::Schema.define(version: 20160509201028) do
   add_index "notes", ["commit_id"], name: "index_notes_on_commit_id", using: :btree
   add_index "notes", ["created_at", "id"], name: "index_notes_on_created_at_and_id", using: :btree
   add_index "notes", ["created_at"], name: "index_notes_on_created_at", using: :btree
-  add_index "notes", ["is_award"], name: "index_notes_on_is_award", using: :btree
   add_index "notes", ["line_code"], name: "index_notes_on_line_code", using: :btree
   add_index "notes", ["note"], name: "index_notes_on_note_trigram", using: :gin, opclasses: {"note"=>"gin_trgm_ops"}
   add_index "notes", ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree
@@ -747,14 +798,15 @@ ActiveRecord::Schema.define(version: 20160509201028) do
 
   create_table "notification_settings", force: :cascade do |t|
     t.integer  "user_id",                 null: false
-    t.integer  "source_id",               null: false
-    t.string   "source_type",             null: false
+    t.integer  "source_id"
+    t.string   "source_type"
     t.integer  "level",       default: 0, null: false
     t.datetime "created_at",              null: false
     t.datetime "updated_at",              null: false
   end
 
   add_index "notification_settings", ["source_id", "source_type"], name: "index_notification_settings_on_source_id_and_source_type", using: :btree
+  add_index "notification_settings", ["user_id", "source_id", "source_type"], name: "index_notifications_on_user_id_and_source_id_and_source_type", unique: true, using: :btree
   add_index "notification_settings", ["user_id"], name: "index_notification_settings_on_user_id", using: :btree
 
   create_table "oauth_access_grants", force: :cascade do |t|
@@ -811,6 +863,18 @@ ActiveRecord::Schema.define(version: 20160509201028) do
 
   add_index "pages_domains", ["domain"], name: "index_pages_domains_on_domain", unique: true, using: :btree
 
+  create_table "path_locks", force: :cascade do |t|
+    t.string   "path",       null: false
+    t.integer  "project_id"
+    t.integer  "user_id"
+    t.datetime "created_at", null: false
+    t.datetime "updated_at", null: false
+  end
+
+  add_index "path_locks", ["path"], name: "index_path_locks_on_path", using: :btree
+  add_index "path_locks", ["project_id"], name: "index_path_locks_on_project_id", using: :btree
+  add_index "path_locks", ["user_id"], name: "index_path_locks_on_user_id", using: :btree
+
   create_table "project_group_links", force: :cascade do |t|
     t.integer  "project_id",                null: false
     t.integer  "group_id",                  null: false
@@ -834,51 +898,49 @@ ActiveRecord::Schema.define(version: 20160509201028) do
     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  "merge_requests_enabled",             default: true,  null: false
+    t.boolean  "wiki_enabled",                       default: true,  null: false
     t.integer  "namespace_id"
-    t.string   "issues_tracker",                   default: "gitlab", null: false
-    t.string   "issues_tracker_id"
-    t.boolean  "snippets_enabled",                 default: true,     null: false
+    t.boolean  "snippets_enabled",                   default: true,  null: false
     t.datetime "last_activity_at"
     t.string   "import_url"
-    t.integer  "visibility_level",                 default: 0,        null: false
-    t.boolean  "archived",                         default: false,    null: false
+    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.float    "repository_size",                    default: 0.0
     t.text     "merge_requests_template"
-    t.integer  "star_count",                       default: 0,        null: false
-    t.boolean  "merge_requests_rebase_enabled",    default: false
+    t.integer  "star_count",                         default: 0,     null: false
+    t.boolean  "merge_requests_rebase_enabled",      default: false
     t.string   "import_type"
     t.string   "import_source"
-    t.integer  "approvals_before_merge",           default: 0,        null: false
-    t.boolean  "reset_approvals_on_push",          default: true
-    t.integer  "commit_count",                     default: 0
-    t.boolean  "merge_requests_ff_only_enabled",   default: false
+    t.integer  "approvals_before_merge",             default: 0,     null: false
+    t.boolean  "reset_approvals_on_push",            default: true
+    t.integer  "commit_count",                       default: 0
+    t.boolean  "merge_requests_ff_only_enabled",     default: false
     t.text     "issues_template"
-    t.boolean  "mirror",                           default: false,    null: false
+    t.boolean  "mirror",                             default: false, null: false
     t.datetime "mirror_last_update_at"
     t.datetime "mirror_last_successful_update_at"
     t.integer  "mirror_user_id"
     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  "mirror_trigger_builds",            default: false,    null: false
-    t.boolean  "pending_delete",                   default: false
-    t.boolean  "public_builds",                    default: true,     null: false
-    t.string   "main_language"
-    t.integer  "pushes_since_gc",                  default: 0
+    t.boolean  "build_allow_git_fetch",              default: true,  null: false
+    t.integer  "build_timeout",                      default: 3600,  null: false
+    t.boolean  "mirror_trigger_builds",              default: false, null: false
+    t.boolean  "pending_delete",                     default: false
+    t.boolean  "public_builds",                      default: true,  null: false
+    t.integer  "pushes_since_gc",                    default: 0
     t.boolean  "last_repository_check_failed"
     t.datetime "last_repository_check_at"
     t.boolean  "container_registry_enabled"
+    t.boolean  "only_allow_merge_if_build_succeeds", default: false, null: false
+    t.boolean  "has_external_issue_tracker"
   end
 
   add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree
@@ -922,7 +984,7 @@ ActiveRecord::Schema.define(version: 20160509201028) do
   create_table "remote_mirrors", force: :cascade do |t|
     t.integer  "project_id"
     t.string   "url"
-    t.boolean  "enabled",                    default: true
+    t.boolean  "enabled",                    default: false
     t.string   "update_status"
     t.datetime "last_update_at"
     t.datetime "last_successful_update_at"
@@ -930,8 +992,8 @@ ActiveRecord::Schema.define(version: 20160509201028) do
     t.text     "encrypted_credentials"
     t.string   "encrypted_credentials_iv"
     t.string   "encrypted_credentials_salt"
-    t.datetime "created_at",                                null: false
-    t.datetime "updated_at",                                null: false
+    t.datetime "created_at",                                 null: false
+    t.datetime "updated_at",                                 null: false
   end
 
   add_index "remote_mirrors", ["project_id"], name: "index_remote_mirrors_on_project_id", using: :btree
@@ -1061,6 +1123,19 @@ ActiveRecord::Schema.define(version: 20160509201028) do
   add_index "todos", ["target_type", "target_id"], name: "index_todos_on_target_type_and_target_id", using: :btree
   add_index "todos", ["user_id"], name: "index_todos_on_user_id", using: :btree
 
+  create_table "u2f_registrations", force: :cascade do |t|
+    t.text     "certificate"
+    t.string   "key_handle"
+    t.string   "public_key"
+    t.integer  "counter"
+    t.integer  "user_id"
+    t.datetime "created_at",  null: false
+    t.datetime "updated_at",  null: false
+  end
+
+  add_index "u2f_registrations", ["key_handle"], name: "index_u2f_registrations_on_key_handle", using: :btree
+  add_index "u2f_registrations", ["user_id"], name: "index_u2f_registrations_on_user_id", using: :btree
+
   create_table "users", force: :cascade do |t|
     t.string   "email",                       default: "",    null: false
     t.string   "encrypted_password",          default: "",    null: false
@@ -1090,7 +1165,6 @@ ActiveRecord::Schema.define(version: 20160509201028) do
     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"
@@ -1134,6 +1208,7 @@ ActiveRecord::Schema.define(version: 20160509201028) do
   add_index "users", ["name"], name: "index_users_on_name", using: :btree
   add_index "users", ["name"], name: "index_users_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
   add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
+  add_index "users", ["state"], name: "index_users_on_state", using: :btree
   add_index "users", ["username"], name: "index_users_on_username", using: :btree
   add_index "users", ["username"], name: "index_users_on_username_trigram", using: :gin, opclasses: {"username"=>"gin_trgm_ops"}
 
@@ -1163,7 +1238,6 @@ ActiveRecord::Schema.define(version: 20160509201028) do
     t.boolean  "note_events",                          default: false,         null: false
     t.boolean  "enable_ssl_verification",              default: true
     t.boolean  "build_events",                         default: false,         null: false
-    t.string   "token"
     t.boolean  "wiki_page_events",                     default: false,         null: false
     t.string   "token"
   end
@@ -1171,5 +1245,8 @@ ActiveRecord::Schema.define(version: 20160509201028) do
   add_index "web_hooks", ["created_at", "id"], name: "index_web_hooks_on_created_at_and_id", using: :btree
   add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree
 
+  add_foreign_key "path_locks", "projects"
+  add_foreign_key "path_locks", "users"
   add_foreign_key "remote_mirrors", "projects"
+  add_foreign_key "u2f_registrations", "users"
 end
diff --git a/doc/README.md b/doc/README.md
index e96efd11e817c9e2237f9bd17b36c70cc9d100fa..79d3e53fe40db3f75d650c463049d7a89cdd54c9 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -14,6 +14,7 @@
 - [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat.
 - [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects.
 - [Analytics](analytics/README.md)
+- [Container Registry](container_registry/README.md) Learn how to use GitLab Container Registry.
 - [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
 - [Webhooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
 - [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
@@ -36,7 +37,7 @@
 - [Restart GitLab](administration/restart_gitlab.md) Learn how to restart GitLab and its components
 - [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages.
 - [Libravatar](customization/libravatar.md) Use Libravatar for user avatars.
-- [Log system](logs/logs.md) Log system.
+- [Log system](administration/logs.md) Log system.
 - [Environment Variables](administration/environment_variables.md) to configure GitLab.
 - [Operations](operations/README.md) Keeping GitLab up and running
 - [Raketasks](raketasks/README.md) Backups, maintenance, automatic webhook setup and the importing of projects.
@@ -59,6 +60,7 @@
 - [High Availability](administration/high_availability/README.md) Configure multiple servers for scaling or high availability
 - [GitLab GEO](gitlab-geo/README.md) Configure GitLab GEO, a
   secondary read-only GitLab instance
+- [Container Registry](administration/container_registry.md) Configure Docker Registry with GitLab
 
 ## Contributor documentation
 
diff --git a/doc/administration/auth/ldap-ee.md b/doc/administration/auth/ldap-ee.md
new file mode 100644
index 0000000000000000000000000000000000000000..b040607e12529b0a2dd0786fe1a3684f83c6e43b
--- /dev/null
+++ b/doc/administration/auth/ldap-ee.md
@@ -0,0 +1,198 @@
+# LDAP Additions in GitLab EE
+
+This is a continuation of the main [LDAP documentation](ldap.md), detailing LDAP
+features specific to GitLab Enterprise Edition.
+
+## User Sync
+
+Once per day, GitLab will run a worker to check and update GitLab
+users against LDAP.
+
+The process will execute the following access checks:
+
+1. Ensure the user is still present in LDAP
+1. If the LDAP server is Active Directory, ensure the user is active (not
+   blocked/disabled state). This will only be checked if
+   `active_directory: true` is set in the LDAP configuration [^1]
+
+The user will be set to `ldap_blocked` state in GitLab if the above conditions
+fail. This means the user will not be able to login or push/pull code.
+
+The process will also update the following user information:
+
+1. Email address
+1. If `sync_ssh_keys` is set, SSH public keys
+1. If Kerberos is enabled, Kerberos identity
+
+> **Note:** The LDAP sync process updates existing users while new users will
+  be created on first sign in.
+
+## Group Sync
+
+If `group_base` is set in LDAP configuration, a group sync process will run
+every hour, on the hour. This allows GitLab group membership to be automatically
+updated based on LDAP group members.
+
+The `group_base` configuration should be a base LDAP 'container', such as an
+'organization' or 'organizational unit', that contains LDAP groups that should
+be available to GitLab. For example, `group_base` could be
+`ou=groups,dc=example,dc=com`. In the config file it will look like the
+following.
+
+**Omnibus configuration**
+
+Edit `/etc/gitlab/gitlab.rb`:
+
+```ruby
+gitlab_rails['ldap_servers'] = YAML.load <<-EOS
+main:
+  # snip...
+  group_base: ou=groups,dc=example,dc=com
+EOS
+```
+
+[Reconfigure GitLab][reconfigure] for the changes to take effect.
+
+**Source configuration**
+
+Edit `/home/git/gitlab/config/gitlab.yml`:
+
+```yaml
+production:
+  ldap:
+    servers:
+      main:
+        # snip...
+        group_base: ou=groups,dc=example,dc=com
+```
+
+[Restart GitLab][restart] for the changes to take effect.
+
+---
+
+To take advantage of group sync, group owners or masters will need to create an
+LDAP group link in their group **Settings -> LDAP Groups** page. Multiple LDAP
+groups can be linked with a single GitLab group. When the link is created, an
+access level/role is specified (Guest, Reporter, Developer, Master, or Owner).
+
+## Administrator Sync
+
+As an extension of group sync, you can automatically manage your global GitLab
+administrators. Specify a group CN for `admin_group` and all members of the
+LDAP group will be given administrator privileges. The configuration will look
+like the following.
+
+> **Note:** Administrators will not be synced unless `group_base` is also
+  specified alongside `admin_group`. Also, only specify the CN of the admin
+  group, as opposed to the full DN.
+
+**Omnibus configuration**
+
+Edit `/etc/gitlab/gitlab.rb`:
+
+```ruby
+gitlab_rails['ldap_servers'] = YAML.load <<-EOS
+main:
+  # snip...
+  group_base: ou=groups,dc=example,dc=com
+  admin_group: my_admin_group
+EOS
+```
+
+[Reconfigure GitLab][reconfigure] for the changes to take effect.
+
+**Source configuration**
+
+Edit `/home/git/gitlab/config/gitlab.yml`:
+
+```yaml
+production:
+  ldap:
+    servers:
+      main:
+        # snip...
+        group_base: ou=groups,dc=example,dc=com
+        admin_group: my_admin_group
+```
+
+[Restart GitLab][restart] for the changes to take effect.
+
+## Group Sync Technical Details
+
+There is a lot going on with group sync 'under the hood'. This section
+outlines what LDAP queries are executed and what behavior you can expect
+from group sync.
+
+Group member access will be downgraded from a higher level if their LDAP group
+membership changes. For example, if a user has 'Owner' rights in a group and the
+next group sync reveals they should only have 'Developer' privileges, their
+access will be adjusted accordingly. The only exception is if the user is the
+*last* owner in a group. Groups need at least one owner to fulfill
+administrative duties.
+
+### Supported LDAP Group Types/Attributes
+
+GitLab supports LDAP groups that use member attributes `member`, `submember`,
+`uniquemember`, `memberof` and `memberuid`. This means group sync supports, at
+least, LDAP groups with object class `groupOfNames`, `posixGroup`, and
+`groupOfUniqueName`. Other object classes should work fine as long as members
+are defined as one of the mentioned attributes. This also means GitLab supports
+Microsoft Active Directory, Apple Open Directory, Open LDAP, and 389 Server.
+Other LDAP servers should work, too.
+
+Active Directory also supports nested groups. Group sync will recursively
+resolve membership if `active_directory: true` is set in the configuration file.
+
+### Queries
+
+- Each LDAP group is queried a maximum of one time with base `group_base` and
+  filter `(cn=<cn_from_group_link>)`.
+- If the LDAP group has the `memberuid` attribute, GitLab will execute another
+  LDAP query per member to obtain each user's full DN. These queries are
+  executed with base `base`, scope 'base object', and a filter depending on
+  whether `user_filter` is set. Filter may be `(uid=<uid_from_group>)` or a
+  joining of `user_filter`.
+
+### Benchmarks
+
+Group sync was written to be as performant as possible. Data is cached, database
+queries are optimized, and LDAP queries are minimized. The last benchmark run
+revealed the following metrics:
+
+For 20,000 LDAP users, 11,000 LDAP groups and 1,000 GitLab groups with 10
+LDAP group links each:
+
+- Initial sync (no existing members assigned in GitLab) took 1.8 hours
+- Subsequent syncs (checking membership, no writes) took 15 minutes
+
+These metrics are meant to provide a baseline and performance may vary based on
+any number of factors. This was a pretty extreme benchmark and most instances will
+not have near this many users or groups. Disk speed, database performance,
+network and LDAP server response time will affect these metrics.
+
+## Troubleshooting
+
+If you see `LDAP search error: Referral` in the logs, or when troubleshooting
+LDAP Group Sync, this error may indicate a configuration problem. The LDAP
+configuration `/etc/gitlab/gitlab.rb` (Omnibus) or `config/gitlab.yml` (source)
+is in YAML format and is sensitive to indentation. Check that `group_base` and
+`admin_group` configuration keys are indented 2 spaces past the server
+identifier. The default identifier is `main` and an example snippet looks like
+the following:
+
+```yaml
+main: # 'main' is the GitLab 'provider ID' of this LDAP server
+  label: 'LDAP'
+  host: 'ldap.example.com'
+  ...
+  group_base: 'cn=my_group,ou=groups,dc=example,dc=com'
+  admin_group: 'my_admin_group'
+```
+
+[reconfigure]: ../restart_gitlab.md#omnibus-gitlab-reconfigure
+[restart]: ../restart_gitlab.md#installations-from-source
+
+[^1]: In Active Directory, a user is marked as disabled/blocked if the user
+      account control attribute (`userAccountControl:1.2.840.113556.1.4.803`)
+      has bit 2 set. See https://ctogonewild.com/2009/09/03/bitmask-searches-in-ldap/
+      for more information.
diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md
index 100967798442c815411aaa1981d569fc3fccffb2..59cdc328693edf1516c668830b60d563c9a7555d 100644
--- a/doc/administration/auth/ldap.md
+++ b/doc/administration/auth/ldap.md
@@ -6,6 +6,11 @@ servers, including Microsoft Active Directory, Apple Open Directory, Open LDAP,
 and 389 Server. GitLab EE includes enhanced integration, including group
 membership syncing.
 
+## GitLab EE
+
+The information on this page is relevent for both GitLab CE and EE. For more
+details about EE-specific LDAP features, see [LDAP EE Documentation](ldap-ee.md).
+
 ## Security
 
 GitLab assumes that LDAP users are not able to change their LDAP 'mail', 'email'
@@ -48,6 +53,11 @@ The configuration inside `gitlab_rails['ldap_servers']` below is sensitive to
 incorrect indentation. Be sure to retain the indentation given in the example.
 Copy/paste can sometimes cause problems.
 
+> **Note:** The `method` value `ssl` corresponds to 'Simple TLS' in the LDAP
+  library. `tls` corresponds to StartTLS, not to be confused with regular TLS.
+  Normally, if you specify `ssl` is will be on port 636 while `tls` (StartTLS)
+  would be on port 389. `plain` also operates on port 389.
+
 **Omnibus configuration**
 
 ```ruby
@@ -130,27 +140,35 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server
     first_name: 'givenName'
     last_name:  'sn'
 
-    ## EE only
+  ## EE only
 
-    # Base where we can search for groups
-    #
-    #   Ex. ou=groups,dc=gitlab,dc=example
-    #
-    group_base: ''
+  # Base where we can search for groups
+  #
+  #   Ex. ou=groups,dc=gitlab,dc=example
+  #
+  group_base: ''
+
+  # The CN of a group containing GitLab administrators
+  #
+  #   Ex. administrators
+  #
+  #   Note: Not `cn=administrators` or the full DN
+  #
+  admin_group: ''
 
-    # The CN of a group containing GitLab administrators
-    #
-    #   Ex. administrators
-    #
-    #   Note: Not `cn=administrators` or the full DN
-    #
-    admin_group: ''
+  # An array of CNs of groups containing users that should be considered external
+  #
+  #   Ex. ['interns', 'contractors']
+  #
+  #   Note: Not `cn=interns` or the full DN
+  #
+  external_groups: []
 
-    # The LDAP attribute containing a user's public SSH key
-    #
-    #   Ex. ssh_public_key
-    #
-    sync_ssh_keys: false
+  # The LDAP attribute containing a user's public SSH key
+  #
+  #   Ex. ssh_public_key
+  #
+  sync_ssh_keys: false
 
 # GitLab EE only: add more LDAP servers
 # Choose an ID made of a-z and 0-9 . This ID will be stored in the database
@@ -184,6 +202,28 @@ production:
         # snip...
 ```
 
+### External Groups
+
+>**Note:** External Groups configuration is only available in GitLab EE Version
+8.9 and above.
+
+Using the `external_groups` setting will allow you to mark all users belonging
+to these groups as [external users](../../permissions/). Group membership is
+checked periodically through the `LdapGroupSync` background task.
+
+**Configuration**
+
+```yaml
+# An array of CNs of groups containing users that should be considered external
+#
+#   Ex. ['interns', 'contractors']
+#
+#   Note: Not `cn=interns` or the full DN
+#
+external_groups: []
+```
+
+
 ## Using an LDAP filter to limit access to your GitLab server
 
 If you want to limit all GitLab access to a subset of the LDAP users on your
@@ -234,24 +274,24 @@ In other words, if an existing GitLab user wants to enable LDAP sign-in for
 themselves, they should check that their GitLab email address matches their
 LDAP email address, and then sign into GitLab via their LDAP credentials.
 
-## Limitations
-
-### TLS Client Authentication
+## Troubleshooting
 
-Not implemented by `Net::LDAP`.
-You should disable anonymous LDAP authentication and enable simple or SASL
-authentication. The TLS client authentication setting in your LDAP server cannot
-be mandatory and clients cannot be authenticated with the TLS protocol.
+### Debug LDAP user filter with ldapsearch
 
-### TLS Server Authentication
+This example uses ldapsearch and assumes you are using ActiveDirectory. The
+following query returns the login names of the users that will be allowed to
+log in to GitLab if you configure your own user_filter.
 
-Not supported by GitLab's configuration options.
-When setting `method: ssl`, the underlying authentication method used by
-`omniauth-ldap` is `simple_tls`.  This method establishes TLS encryption with
-the LDAP server before any LDAP-protocol data is exchanged but no validation of
-the LDAP server's SSL certificate is performed.
+```
+ldapsearch -H ldaps://$host:$port -D "$bind_dn" -y bind_dn_password.txt  -b "$base" "(&(ObjectClass=User)($user_filter))" sAMAccountName
+```
 
-## Troubleshooting
+- Variables beginning with a `$` refer to a variable from the LDAP section of
+  your configuration file.
+- Replace ldaps:// with ldap:// if you are using the plain authentication method.
+  Port `389` is the default `ldap://` port and `636` is the default `ldaps://`
+  port.
+- We are assuming the password for the bind_dn user is in bind_dn_password.txt.
 
 ### Invalid credentials when logging in
 
diff --git a/doc/administration/container_registry.md b/doc/administration/container_registry.md
new file mode 100644
index 0000000000000000000000000000000000000000..7870669fa7767973958132db165a6b2d3c781101
--- /dev/null
+++ b/doc/administration/container_registry.md
@@ -0,0 +1,375 @@
+# GitLab Container Registry Administration
+
+> **Note:**
+This feature was [introduced][ce-4040] in GitLab 8.8.
+
+With the Docker Container Registry integrated into GitLab, every project can
+have its own space to store its Docker images.
+
+You can read more about Docker Registry at https://docs.docker.com/registry/introduction/.
+
+---
+
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+
+- [Enable the Container Registry](#enable-the-container-registry)
+- [Container Registry domain configuration](#container-registry-domain-configuration)
+    - [Configure Container Registry under an existing GitLab domain](#configure-container-registry-under-an-existing-gitlab-domain)
+    - [Configure Container Registry under its own domain](#configure-container-registry-under-its-own-domain)
+- [Disable Container Registry site-wide](#disable-container-registry-site-wide)
+- [Disable Container Registry per project](#disable-container-registry-per-project)
+- [Disable Container Registry for new projects site-wide](#disable-container-registry-for-new-projects-site-wide)
+- [Container Registry storage path](#container-registry-storage-path)
+- [Storage limitations](#storage-limitations)
+- [Changelog](#changelog)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+## Enable the Container Registry
+
+**Omnibus GitLab installations**
+
+All you have to do is configure the domain name under which the Container
+Registry will listen to. Read [#container-registry-domain-configuration](#container-registry-domain-configuration)
+and pick one of the two options that fits your case.
+
+>**Note:**
+The container Registry works under HTTPS by default. Using HTTP is possible
+but not recommended and out of the scope of this document.
+Read the [insecure Registry documentation][docker-insecure] if you want to
+implement this.
+
+---
+
+**Installations from source**
+
+If you have installed GitLab from source:
+
+1. You will have to [install Docker Registry][registry-deploy] by yourself.
+1. After the installation is complete, you will have to configure the Registry's
+   settings in `gitlab.yml` in order to enable it.
+1. Use the sample NGINX configuration file that is found under
+   [`lib/support/nginx/registry-ssl`][registry-ssl] and edit it to match the
+   `host`, `port` and TLS certs paths.
+
+The contents of `gitlab.yml` are:
+
+```
+registry:
+  enabled: true
+  host: registry.gitlab.example.com
+  port: 5005
+  api_url: http://localhost:5000/
+  key: config/registry.key
+  path: shared/registry
+  issuer: gitlab-issuer
+```
+
+where:
+
+| Parameter | Description |
+| --------- | ----------- |
+| `enabled` | `true` or `false`. Enables the Registry in GitLab. By default this is `false`. |
+| `host`    | The host URL under which the Registry will run and the users will be able to use. |
+| `port`    | The port under which the external Registry domain will listen on. |
+| `api_url` | The internal API URL under which the Registry is exposed to. It defaults to `http://localhost:5000`. |
+| `key`     | The private key location that is a pair of Registry's `rootcertbundle`. Read the [token auth configuration documentation][token-config]. |
+| `path`    | This should be the same directory like specified in Registry's `rootdirectory`. Read the [storage configuration documentation][storage-config]. This path needs to be readable by the GitLab user, the web-server user and the Registry user. Read more in [#container-registry-storage-path](#container-registry-storage-path). |
+| `issuer`  | This should be the same value as configured in Registry's `issuer`. Read the [token auth configuration documentation][token-config]. |
+
+>**Note:**
+GitLab does not ship with a Registry init file. Hence, [restarting GitLab][restart gitlab]
+will not restart the Registry should you modify its settings. Read the upstream
+documentation on how to achieve that.
+
+## Container Registry domain configuration
+
+There are two ways you can configure the Registry's external domain.
+
+- Either [use the existing GitLab domain][existing-domain] where in that case
+  the Registry will have to listen on a port and reuse GitLab's TLS certificate,
+- or [use a completely separate domain][new-domain] with a new TLS certificate
+  for that domain.
+
+Since the container Registry requires a TLS certificate, in the end it all boils
+down to how easy or pricey is to get a new one.
+
+Please take this into consideration before configuring the Container Registry
+for the first time.
+
+### Configure Container Registry under an existing GitLab domain
+
+If the Registry is configured to use the existing GitLab domain, you can
+expose the Registry on a port so that you can reuse the existing GitLab TLS
+certificate.
+
+Assuming that the GitLab domain is `https://gitlab.example.com` and the port the
+Registry is exposed to the outside world is `4567`, here is what you need to set
+in `gitlab.rb` or `gitlab.yml` if you are using Omnibus GitLab or installed
+GitLab from source respectively.
+
+---
+
+**Omnibus GitLab installations**
+
+1. Your `/etc/gitlab/gitlab.rb` should contain the Registry URL as well as the
+   path to the existing TLS certificate and key used by GitLab:
+
+    ```ruby
+    registry_external_url 'https://gitlab.example.com:4567'
+    ```
+
+    Note how the `registry_external_url` is listening on HTTPS under the
+    existing GitLab URL, but on a different port.
+
+    If your TLS certificate is not in `/etc/gitlab/ssl/gitlab.example.com.crt`
+    and key not in `/etc/gitlab/ssl/gitlab.example.com.key` uncomment the lines
+    below:
+
+    ```ruby
+    registry_nginx['ssl_certificate'] = "/path/to/certificate.pem"
+    registry_nginx['ssl_certificate_key'] = "/path/to/certificate.key"
+    ```
+
+1. Save the file and [reconfigure GitLab][] for the changes to take effect.
+
+---
+
+**Installations from source**
+
+1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
+   configure it with the following settings:
+
+    ```
+    registry:
+      enabled: true
+      host: gitlab.example.com
+      port: 4567
+    ```
+
+1. Save the file and [restart GitLab][] for the changes to take effect.
+1. Make the relevant changes in NGINX as well (domain, port, TLS certificates path).
+
+---
+
+Users should now be able to login to the Container Registry with their GitLab
+credentials using:
+
+```bash
+docker login gitlab.example.com:4567
+```
+
+### Configure Container Registry under its own domain
+
+If the Registry is configured to use its own domain, you will need a TLS
+certificate for that specific domain (e.g., `registry.example.com`) or maybe
+a wildcard certificate if hosted under a subdomain  of your existing GitLab
+domain (e.g., `registry.gitlab.example.com`).
+
+Let's assume that you want the container Registry to be accessible at
+`https://registry.gitlab.example.com`.
+
+---
+
+**Omnibus GitLab installations**
+
+1. Place your TLS certificate and key in
+   `/etc/gitlab/ssl/registry.gitlab.example.com.crt` and
+   `/etc/gitlab/ssl/registry.gitlab.example.com.key` and make sure they have
+   correct permissions:
+
+    ```bash
+    chmod 600 /etc/gitlab/ssl/registry.gitlab.example.com.*
+    ```
+
+1. Once the TLS certificate is in place, edit `/etc/gitlab/gitlab.rb` with:
+
+    ```ruby
+    registry_external_url 'https://registry.gitlab.example.com'
+    ```
+
+    Note how the `registry_external_url` is listening on HTTPS.
+
+1. Save the file and [reconfigure GitLab][] for the changes to take effect.
+
+> **Note:**
+If you have a [wildcard certificate][], you need to specify the path to the
+certificate in addition to the URL, in this case `/etc/gitlab/gitlab.rb` will
+look like:
+>
+```ruby
+registry_nginx['ssl_certificate'] = "/etc/gitlab/ssl/certificate.pem"
+registry_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/certificate.key"
+```
+
+---
+
+**Installations from source**
+
+1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
+   configure it with the following settings:
+
+    ```
+    registry:
+      enabled: true
+      host: registry.gitlab.example.com
+    ```
+
+1. Save the file and [restart GitLab][] for the changes to take effect.
+1. Make the relevant changes in NGINX as well (domain, port, TLS certificates path).
+
+---
+
+Users should now be able to login to the Container Registry using their GitLab
+credentials:
+
+```bash
+docker login registry.gitlab.example.com
+```
+
+## Disable Container Registry site-wide
+
+>**Note:**
+Disabling the Registry in the Rails GitLab application as set by the following
+steps, will not remove any existing Docker images. This is handled by the
+Registry application itself.
+
+**Omnibus GitLab**
+
+1. Open `/etc/gitlab/gitlab.rb` and set `registry['enable']` to `false`:
+
+    ```ruby
+    registry['enable'] = false
+    ```
+
+1. Save the file and [reconfigure GitLab][] for the changes to take effect.
+
+---
+
+**Installations from source**
+
+1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
+   set `enabled` to `false`:
+
+    ```
+    registry:
+      enabled: false
+    ```
+
+1. Save the file and [restart GitLab][] for the changes to take effect.
+
+## Disable Container Registry per project
+
+If Registry is enabled in your GitLab instance, but you don't need it for your
+project, you can disable it from your project's settings. Read the user guide
+on how to achieve that.
+
+## Disable Container Registry for new projects site-wide
+
+If the Container Registry is enabled, then it will be available on all new
+projects. To disable this function and let the owners of a project to enable
+the Container Registry by themselves, follow the steps below.
+
+---
+
+**Omnibus GitLab installations**
+
+1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
+
+    ```ruby
+    gitlab_rails['gitlab_default_projects_features_container_registry'] = false
+    ```
+
+1. Save the file and [reconfigure GitLab][] for the changes to take effect.
+
+---
+
+**Installations from source**
+
+1. Open `/home/git/gitlab/config/gitlab.yml`, find the `default_projects_features`
+   entry and configure it so that `container_registry` is set to `false`:
+
+    ```
+    ## Default project features settings
+    default_projects_features:
+      issues: true
+      merge_requests: true
+      wiki: true
+      snippets: false
+      builds: true
+      container_registry: false
+    ```
+
+1. Save the file and [restart GitLab][] for the changes to take effect.
+
+## Container Registry storage path
+
+To change the storage path where Docker images will be stored, follow the
+steps below.
+
+This path is accessible to:
+
+- the user running the Container Registry daemon,
+- the user running GitLab
+
+> **Warning** You should confirm that all GitLab, Registry and web server users
+have access to this directory.
+
+---
+
+**Omnibus GitLab installations**
+
+The default location where images are stored in Omnibus, is
+`/var/opt/gitlab/gitlab-rails/shared/registry`. To change it:
+
+1. Edit `/etc/gitlab/gitlab.rb`:
+
+    ```ruby
+    gitlab_rails['registry_path'] = "/path/to/registry/storage"
+    ```
+
+1. Save the file and [reconfigure GitLab][] for the changes to take effect.
+
+---
+
+**Installations from source**
+
+The default location where images are stored in source installations, is
+`/home/git/gitlab/shared/registry`. To change it:
+
+1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
+   change the `path` setting:
+
+    ```
+    registry:
+      path: shared/registry
+    ```
+
+1. Save the file and [restart GitLab][] for the changes to take effect.
+
+## Storage limitations
+
+Currently, there is no storage limitation, which means a user can upload an
+infinite amount of Docker images with arbitrary sizes. This setting will be
+configurable in future releases.
+
+## Changelog
+
+**GitLab 8.8 ([source docs][8-8-docs])**
+
+- GitLab Container Registry feature was introduced.
+
+[reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure
+[restart gitlab]: restart_gitlab.md#installations-from-source
+[wildcard certificate]: https://en.wikipedia.org/wiki/Wildcard_certificate
+[ce-4040]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4040
+[docker-insecure]: https://docs.docker.com/registry/insecure/
+[registry-deploy]: https://docs.docker.com/registry/deploying/
+[storage-config]: https://docs.docker.com/registry/configuration/#storage
+[token-config]: https://docs.docker.com/registry/configuration/#token
+[8-8-docs]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-8-stable/doc/administration/container_registry.md
+[registry-ssl]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/support/nginx/registry-ssl
+[existing-domain]: #configure-container-registry-under-an-existing-gitlab-domain
+[new-domain]: #configure-container-registry-under-its-own-domain
diff --git a/doc/administration/high_availability/README.md b/doc/administration/high_availability/README.md
index 43d85ffb7752cede8d9fe89ee27036c4fda660ca..d74a786ac241420289e68e881ff181b97a7fb193 100644
--- a/doc/administration/high_availability/README.md
+++ b/doc/administration/high_availability/README.md
@@ -19,6 +19,8 @@ Components/Servers Required:
 
 - 2 servers/virtual machines (one active/one passive)
 
+![Active/Passive HA Diagram](../img/high_availability/active-passive-diagram.png)
+
 ### Active/Active
 
 This architecture scales easily because all application servers handle
@@ -26,6 +28,8 @@ user requests simultaneously. The database, Redis, and GitLab application are
 all deployed on separate servers. The configuration is **only** highly-available
 if the database, Redis and storage are also configured as such.
 
+![Active/Active HA Diagram](../img/high_availability/active-active-diagram.png)
+
 **Steps to configure active/active:**
 
 1. [Configure the database](database.md)
diff --git a/doc/administration/high_availability/nfs.md b/doc/administration/high_availability/nfs.md
index 49ff5d536a10cddaaa3cff9640644ee820cac921..537f4f3501de539090ce67323184a25095fb78b6 100644
--- a/doc/administration/high_availability/nfs.md
+++ b/doc/administration/high_availability/nfs.md
@@ -2,8 +2,8 @@
 
 ## Required NFS Server features
 
-**File locking**: GitLab **requires** file locking which is only supported
-natively in NFS version 4. NFSv3 also supports locking as long as
+**File locking**: GitLab **requires** advisory file locking, which is only
+supported natively in NFS version 4. NFSv3 also supports locking as long as
 Linux Kernel 2.6.5+ is used. We recommend using version 4 and do not
 specifically test NFSv3.
 
diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md
index d89a1e582cacaa18005c26556f97cdd9f7825fa5..f6153216f333a9a4d0c573415659d28084e468fb 100644
--- a/doc/administration/high_availability/redis.md
+++ b/doc/administration/high_availability/redis.md
@@ -26,7 +26,7 @@ that runs Redis.
     ```ruby
       external_url 'https://gitlab.example.com'
 
-      # Disable all components except PostgreSQL
+      # Disable all components except Redis
       redis['enable'] = true
       bootstrap['enable'] = false
       nginx['enable'] = false
diff --git a/doc/administration/img/high_availability/active-active-diagram.png b/doc/administration/img/high_availability/active-active-diagram.png
new file mode 100644
index 0000000000000000000000000000000000000000..81259e0ae9348a16e66407946719ddf3d75d974b
Binary files /dev/null and b/doc/administration/img/high_availability/active-active-diagram.png differ
diff --git a/doc/administration/img/high_availability/active-passive-diagram.png b/doc/administration/img/high_availability/active-passive-diagram.png
new file mode 100644
index 0000000000000000000000000000000000000000..f69ff1d0357d3141eee27f22770c6958a2469ea2
Binary files /dev/null and b/doc/administration/img/high_availability/active-passive-diagram.png differ
diff --git a/doc/administration/logs.md b/doc/administration/logs.md
new file mode 100644
index 0000000000000000000000000000000000000000..737b39db16ceb1966f6349b1a49af400104df660
--- /dev/null
+++ b/doc/administration/logs.md
@@ -0,0 +1,137 @@
+## Log system
+
+GitLab has an advanced log system where everything is logged so that you
+can analyze your instance using various system log files. In addition to
+system log files, GitLab Enterprise Edition comes with Audit Events.
+Find more about them [in Audit Events
+documentation](http://docs.gitlab.com/ee/administration/audit_events.html)
+
+System log files are typically plain text in a standard log file format.
+This guide talks about how to read and use these system log files.
+
+### production.log
+
+This file lives in `/var/log/gitlab/gitlab-rails/production.log` for
+omnibus package or in `/home/git/gitlab/log/production.log` for
+installations from source.
+
+It contains information about all performed requests. You can see the
+URL and type of request, IP address and what exactly parts of code were
+involved to service this particular request. Also you can see all SQL
+request that have been performed and how much time it took. This task is
+more useful for GitLab contributors and developers. Use part of this log
+file when you are going to report bug. For example:
+
+```
+Started GET "/gitlabhq/yaml_db/tree/master" for 168.111.56.1 at 2015-02-12 19:34:53 +0200
+Processing by Projects::TreeController#show as HTML
+  Parameters: {"project_id"=>"gitlabhq/yaml_db", "id"=>"master"}
+
+  ... [CUT OUT]
+
+  Namespaces"."created_at" DESC, "namespaces"."id" DESC LIMIT 1 [["id", 26]]
+  CACHE (0.0ms) SELECT  "members".* FROM "members"  WHERE "members"."source_type" = 'Project' AND "members"."type" IN ('ProjectMember') AND "members"."source_id" = $1 AND "members"."source_type" = $2 AND "members"."user_id" = 1  ORDER BY "members"."created_at" DESC, "members"."id" DESC LIMIT 1  [["source_id", 18], ["source_type", "Project"]]
+  CACHE (0.0ms) SELECT  "members".* FROM "members"  WHERE "members"."source_type" = 'Project' AND "members".
+  (1.4ms) SELECT COUNT(*) FROM "merge_requests"  WHERE "merge_requests"."target_project_id" = $1 AND ("merge_requests"."state" IN ('opened','reopened')) [["target_project_id", 18]]
+  Rendered layouts/nav/_project.html.haml (28.0ms)
+  Rendered layouts/_collapse_button.html.haml (0.2ms)
+  Rendered layouts/_flash.html.haml (0.1ms)
+  Rendered layouts/_page.html.haml (32.9ms)
+Completed 200 OK in 166ms (Views: 117.4ms | ActiveRecord: 27.2ms)
+```
+
+In this example we can see that server processed an HTTP request with URL
+`/gitlabhq/yaml_db/tree/master` from IP 168.111.56.1 at 2015-02-12
+19:34:53 +0200. Also we can see that request was processed by
+`Projects::TreeController`.
+
+### application.log
+
+This file lives in `/var/log/gitlab/gitlab-rails/application.log` for
+omnibus package or in `/home/git/gitlab/log/application.log` for
+installations from source.
+
+It helps you discover events happening in your instance such as user creation,
+project removing and so on. For example:
+
+```
+October 06, 2014 11:56: User "Administrator" (admin@example.com) was created
+October 06, 2014 11:56: Documentcloud created a new project "Documentcloud / Underscore"
+October 06, 2014 11:56: Gitlab Org created a new project "Gitlab Org / Gitlab Ce"
+October 07, 2014 11:25: User "Claudie Hodkiewicz" (nasir_stehr@olson.co.uk)  was removed
+October 07, 2014 11:25: Project "project133" was removed
+```
+
+### githost.log
+
+This file lives in `/var/log/gitlab/gitlab-rails/githost.log` for
+omnibus package or in `/home/git/gitlab/log/githost.log` for
+installations from source.
+
+GitLab has to interact with Git repositories but in some rare cases
+something can go wrong and in this case you will know what exactly
+happened. This log file contains all failed requests from GitLab to Git
+repositories. In the majority of cases this file will be useful for developers
+only. For example:
+
+```
+December 03, 2014 13:20 -> ERROR -> Command failed [1]: /usr/bin/git --git-dir=/Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/gitlab-satellites/group184/gitlabhq/.git --work-tree=/Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/gitlab-satellites/group184/gitlabhq merge --no-ff -mMerge branch 'feature_conflict' into 'feature' source/feature_conflict
+
+error: failed to push some refs to '/Users/vsizov/gitlab-development-kit/repositories/gitlabhq/gitlab_git.git'
+```
+
+### sidekiq.log
+
+This file lives in `/var/log/gitlab/gitlab-rails/sidekiq.log` for
+omnibus package or in `/home/git/gitlab/log/sidekiq.log` for
+installations from source.
+
+GitLab uses background jobs for processing tasks which can take a long
+time. All information about processing these jobs are written down to
+this file. For example:
+
+```
+2014-06-10T07:55:20Z 2037 TID-tm504 ERROR: /opt/bitnami/apps/discourse/htdocs/vendor/bundle/ruby/1.9.1/gems/redis-3.0.7/lib/redis/client.rb:228:in `read'
+2014-06-10T18:18:26Z 14299 TID-55uqo INFO: Booting Sidekiq 3.0.0 with redis options {:url=>"redis://localhost:6379/0", :namespace=>"sidekiq"}
+```
+
+### gitlab-shell.log
+
+This file lives in `/var/log/gitlab/gitlab-shell/gitlab-shell.log` for
+omnibus package or in `/home/git/gitlab-shell/gitlab-shell.log` for
+installations from source.
+
+GitLab shell is used by Gitlab for executing Git commands and provide
+SSH access to Git repositories. For example:
+
+```
+I, [2015-02-13T06:17:00.671315 #9291]  INFO -- : Adding project root/example.git at </var/opt/gitlab/git-data/repositories/root/dcdcdcdcd.git>.
+I, [2015-02-13T06:17:00.679433 #9291]  INFO -- : Moving existing hooks directory and symlinking global hooks directory for /var/opt/gitlab/git-data/repositories/root/example.git.
+```
+
+### unicorn\_stderr.log
+
+This file lives in `/var/log/gitlab/unicorn/unicorn_stderr.log` for
+omnibus package or in `/home/git/gitlab/log/unicorn_stderr.log` for
+installations from source.
+
+Unicorn is a high-performance forking Web server which is used for
+serving the GitLab application. You can look at this log if, for
+example, your application does not respond. This log contains all
+information about the state of unicorn processes at any given time.
+
+```
+I, [2015-02-13T06:14:46.680381 #9047]  INFO -- : Refreshing Gem list
+I, [2015-02-13T06:14:56.931002 #9047]  INFO -- : listening on addr=127.0.0.1:8080 fd=12
+I, [2015-02-13T06:14:56.931381 #9047]  INFO -- : listening on addr=/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket fd=13
+I, [2015-02-13T06:14:56.936638 #9047]  INFO -- : master process ready
+I, [2015-02-13T06:14:56.946504 #9092]  INFO -- : worker=0 spawned pid=9092
+I, [2015-02-13T06:14:56.946943 #9092]  INFO -- : worker=0 ready
+I, [2015-02-13T06:14:56.947892 #9094]  INFO -- : worker=1 spawned pid=9094
+I, [2015-02-13T06:14:56.948181 #9094]  INFO -- : worker=1 ready
+W, [2015-02-13T07:16:01.312916 #9094]  WARN -- : #<Unicorn::HttpServer:0x0000000208f618>: worker (pid: 9094) exceeds memory limit (320626688 bytes > 247066940 bytes)
+W, [2015-02-13T07:16:01.313000 #9094]  WARN -- : Unicorn::WorkerKiller send SIGQUIT (pid: 9094) alive: 3621 sec (trial 1)
+I, [2015-02-13T07:16:01.530733 #9047]  INFO -- : reaped #<Process::Status: pid 9094 exit 0> worker=1
+I, [2015-02-13T07:16:01.534501 #13379]  INFO -- : worker=1 spawned pid=13379
+I, [2015-02-13T07:16:01.534848 #13379]  INFO -- : worker=1 ready
+```
diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md
index 3411e4af6a7c7e6b30071477b4859729df7dee86..4172b604cec81ebcc3098f152c8371c03ec98611 100644
--- a/doc/administration/repository_checks.md
+++ b/doc/administration/repository_checks.md
@@ -5,7 +5,7 @@ This feature was [introduced][ce-3232] in GitLab 8.7. It is OFF by
 default because it still causes too many false alarms.
 
 Git has a built-in mechanism, [git fsck][git-fsck], to verify the
-integrity of all data commited to a repository. GitLab administrators
+integrity of all data committed to a repository. GitLab administrators
 can trigger such a check for a project via the project page under the
 admin panel. The checks run asynchronously so it may take a few minutes
 before the check result is visible on the project admin page. If the
@@ -41,4 +41,4 @@ alarms you can choose to clear ALL repository check states from the
 
 ---
 [ce-3232]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3232 "Auto git fsck"
-[git-fsck]: https://www.kernel.org/pub/software/scm/git/docs/git-fsck.html "git fsck documentation"
\ No newline at end of file
+[git-fsck]: https://www.kernel.org/pub/software/scm/git/docs/git-fsck.html "git fsck documentation"
diff --git a/doc/administration/troubleshooting/sidekiq.md b/doc/administration/troubleshooting/sidekiq.md
index 134a7583762dc0e2153437746498744b6f12e924..b71f8fabbc84267bd1c18227635b0ffc1e4f5c64 100644
--- a/doc/administration/troubleshooting/sidekiq.md
+++ b/doc/administration/troubleshooting/sidekiq.md
@@ -147,7 +147,16 @@ bt
 To output a backtrace from all threads at once:
 
 ```
-apply all thread bt
+set pagination off
+thread apply all bt
+```
+
+Once you're done debugging with `gdb`, be sure to detach from the process and
+exit:
+
+```
+detach
+exit
 ```
 
 ## Check for blocking queries
diff --git a/doc/api/README.md b/doc/api/README.md
index fd9fd7ebc993e2e5d11c7cc97b4f22a4f731312e..c94b07d77279edca2eb20d5735fa3af84b77924d 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -8,33 +8,42 @@ under [`/lib/api`](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/api).
 Documentation for various API resources can be found separately in the
 following locations:
 
-- [Users](users.md)
-- [Session](session.md)
-- [Projects](projects.md) including setting Webhooks
-- [Project Snippets](project_snippets.md)
-- [Services](services.md)
-- [Repositories](repositories.md)
-- [Repository Files](repository_files.md)
-- [Commits](commits.md)
-- [Tags](tags.md)
 - [Branches](branches.md)
-- [Merge Requests](merge_requests.md)
+- [Builds](builds.md)
+- [Build triggers](build_triggers.md)
+- [Build Variables](build_variables.md)
+- [Commits](commits.md)
+- [Deploy Keys](deploy_keys.md)
+- [Groups](groups.md)
 - [Issues](issues.md)
+- [Keys](keys.md)
 - [Labels](labels.md)
+- [License](license.md)
+- [Merge Requests](merge_requests.md)
 - [Milestones](milestones.md)
-- [Notes](notes.md) (comments)
-- [Deploy Keys](deploy_keys.md)
-- [System Hooks](system_hooks.md)
-- [Groups](groups.md)
+- [Open source license templates](licenses.md)
 - [Namespaces](namespaces.md)
-- [Settings](settings.md)
-- [Keys](keys.md)
-- [Builds](builds.md)
-- [Build triggers](build_triggers.md)
-- [Build Variables](build_variables.md)
-- [Runners](runners.md)
+- [Notes](notes.md) (comments)
 - [Open source license templates](licenses.md)
-- [License](license.md)
+- [Projects](projects.md) including setting Webhooks
+- [Project Snippets](project_snippets.md)
+- [Repositories](repositories.md)
+- [Repository Files](repository_files.md)
+- [Runners](runners.md)
+- [Services](services.md)
+- [Session](session.md)
+- [Settings](settings.md)
+- [System Hooks](system_hooks.md)
+- [Tags](tags.md)
+- [Users](users.md)
+
+### Internal CI API
+
+The following documentation is for the [internal CI API](ci/README.md):
+
+- [Builds](ci/builds.md)
+- [Runners](ci/runners.md)
+>>>>>>> ce/master
 
 ## Authentication
 
diff --git a/doc/api/builds.md b/doc/api/builds.md
index 4c0a47d1ea04eaddaf83a79b222e127f30083b9a..de9989443528fc425a86b581739d0695637a371e 100644
--- a/doc/api/builds.md
+++ b/doc/api/builds.md
@@ -21,85 +21,85 @@ 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",
-        "artifacts_file": {
-          "filename": "artifacts.zip",
-          "size": 1000
-        },
-        "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.802Z",
+    "artifacts_file": {
+      "filename": "artifacts.zip",
+      "size": 1000
     },
-    {
-        "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",
-        "artifacts_file": 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": ""
-        }
+    "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",
+    "artifacts_file": 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": ""
+    }
+  }
 ]
 ```
 
@@ -125,68 +125,68 @@ 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",
-        "artifacts_file": 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": "2016-01-11T10:13:33.506Z",
+    "artifacts_file": 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."
     },
-    {
-        "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",
-        "artifacts_file": 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": ""
-        }
+    "coverage": null,
+    "created_at": "2015-12-24T15:51:21.957Z",
+    "artifacts_file": 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": ""
     }
+  }
 ]
 ```
 
@@ -211,42 +211,42 @@ 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",
-    "artifacts_file": 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": ""
-    }
+  "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",
+  "artifacts_file": 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": ""
+  }
 }
 ```
 
@@ -278,6 +278,30 @@ Response:
 
 [ce-2893]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2893
 
+## Get a trace file
+
+Get a trace of a specific build of a project
+
+```
+GET /projects/:id/builds/:build_id/trace
+```
+
+| Attribute  | Type    | Required | Description         |
+|------------|---------|----------|---------------------|
+| id         | integer | yes      | The ID of a project |
+| build_id   | integer | yes      | The ID of a build   |
+
+```
+curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/8/trace"
+```
+
+Response:
+
+| Status    | Description                       |
+|-----------|-----------------------------------|
+| 200       | Serves the trace file             |
+| 404       | Build not found or no trace file  |
+
 ## Cancel a build
 
 Cancel a single build of a project
@@ -299,28 +323,28 @@ 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",
-    "artifacts_file": 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": "2016-01-11T10:13:33.506Z",
+  "artifacts_file": 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
 }
 ```
 
@@ -345,28 +369,28 @@ 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",
-    "artifacts_file": null,
-    "finished_at": null,
-    "id": 69,
-    "name": "rubocop",
-    "ref": "master",
-    "runner": null,
-    "stage": "test",
-    "started_at": null,
-    "status": "pending",
-    "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": "2016-01-11T10:13:33.506Z",
+  "artifacts_file": null,
+  "finished_at": null,
+  "id": 69,
+  "name": "rubocop",
+  "ref": "master",
+  "runner": null,
+  "stage": "test",
+  "started_at": null,
+  "status": "pending",
+  "tag": false,
+  "user": null
 }
 ```
 
@@ -395,27 +419,77 @@ 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,
-    "download_url": null,
-    "id": 69,
-    "name": "rubocop",
-    "ref": "master",
-    "runner": null,
-    "stage": "test",
-    "created_at": "2016-01-11T10:13:33.506Z",
-    "started_at": "2016-01-11T10:13:33.506Z",
-    "finished_at": "2016-01-11T10:15:10.506Z",
-    "status": "failed",
-    "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,
+  "download_url": null,
+  "id": 69,
+  "name": "rubocop",
+  "ref": "master",
+  "runner": null,
+  "stage": "test",
+  "created_at": "2016-01-11T10:13:33.506Z",
+  "started_at": "2016-01-11T10:13:33.506Z",
+  "finished_at": "2016-01-11T10:15:10.506Z",
+  "status": "failed",
+  "tag": false,
+  "user": null
+}
+```
+
+## Keep artifacts
+
+Prevents artifacts from being deleted when expiration is set.
+
+```
+POST /projects/:id/builds/:build_id/artifacts/keep
+```
+
+Parameters
+
+| Attribute   | Type    | required | Description         |
+|-------------|---------|----------|---------------------|
+| `id`        | integer | yes      | The ID of a project |
+| `build_id`  | integer | yes      | The ID of a build   |
+
+Example request:
+
+```
+curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/1/artifacts/keep"
+```
+
+Example 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,
+  "download_url": null,
+  "id": 69,
+  "name": "rubocop",
+  "ref": "master",
+  "runner": null,
+  "stage": "test",
+  "created_at": "2016-01-11T10:13:33.506Z",
+  "started_at": "2016-01-11T10:13:33.506Z",
+  "finished_at": "2016-01-11T10:15:10.506Z",
+  "status": "failed",
+  "tag": false,
+  "user": null
 }
 ```
diff --git a/doc/api/ci/README.md b/doc/api/ci/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..96a281e27c8c3ac44319981df928ece351757328
--- /dev/null
+++ b/doc/api/ci/README.md
@@ -0,0 +1,24 @@
+# GitLab CI API
+
+## Purpose
+
+The main purpose of GitLab CI API is to provide the necessary data and context
+for GitLab CI Runners.
+
+All relevant information about the consumer API can be found in a
+[separate document](../../api/README.md).
+
+## API Prefix
+
+The current CI API prefix is `/ci/api/v1`.
+
+You need to prepend this prefix to all examples in this documentation, like:
+
+```bash
+GET /ci/api/v1/builds/:id/artifacts
+```
+
+## Resources
+
+- [Builds](builds.md)
+- [Runners](runners.md)
diff --git a/doc/api/ci/builds.md b/doc/api/ci/builds.md
new file mode 100644
index 0000000000000000000000000000000000000000..d779463fd8cb047f17669399fece03f1249cc43f
--- /dev/null
+++ b/doc/api/ci/builds.md
@@ -0,0 +1,138 @@
+# Builds API
+
+API used by runners to receive and update builds.
+
+>**Note:**
+This API is intended to be used only by Runners as their own
+communication channel. For the consumer API see the
+[Builds API](../builds.md).
+
+## Authentication
+
+This API uses two types of authentication:
+
+1. Unique Runner's token which is the token assigned to the Runner after it
+   has been registered.
+
+2. Using the build authorization token.
+   This is project's CI token that can be found under the **Builds** section of
+   a project's settings. The build authorization token can be passed as a
+   parameter or a value of `BUILD-TOKEN` header.
+
+These two methods of authentication are interchangeable.
+
+## Builds
+
+### Runs oldest pending build by runner
+
+```
+POST /ci/api/v1/builds/register
+```
+
+| Attribute | Type    | Required | Description         |
+|-----------|---------|----------|---------------------|
+| `token`   | string  | yes      | Unique runner token |
+
+
+```
+curl -X POST "https://gitlab.example.com/ci/api/v1/builds/register" -F "token=t0k3n"
+```
+
+### Update details of an existing build
+
+```
+PUT /ci/api/v1/builds/:id
+```
+
+| Attribute | Type    | Required | Description          |
+|-----------|---------|----------|----------------------|
+| `id`      | integer | yes      | The ID of a project  |
+| `token`   | string  | yes      | Unique runner token  |
+| `state`   | string  | no       | The state of a build |
+| `trace`   | string  | no       | The trace of a build |
+
+```
+curl -X PUT "https://gitlab.example.com/ci/api/v1/builds/1234" -F "token=t0k3n" -F "state=running" -F "trace=Running git clone...\n"
+```
+
+### Incremental build trace update
+
+Using this method you need to send trace content as a request body. You also need to provide the `Content-Range` header
+with a range of sent trace part. Note that you need to send parts in the proper order, so the begining of the part
+must start just after the end of the previous part. If you provide the wrong part, then GitLab CI API will return `416
+Range Not Satisfiable` response with a header `Range: 0-X`, where `X` is the current trace length.
+
+For example, if you receive `Range: 0-11` in the response, then your next part must contain a `Content-Range: 11-...`
+header and a trace part covered by this range.
+
+For a valid update API will return `202` response with:
+* `Build-Status: {status}` header containing current status of the build,
+* `Range: 0-{length}` header with the current trace length.
+
+```
+PATCH /ci/api/v1/builds/:id/trace.txt
+```
+
+Parameters:
+
+| Attribute | Type    | Required | Description          |
+|-----------|---------|----------|----------------------|
+| `id`      | integer | yes      | The ID of a build    |
+
+Headers:
+
+| Attribute       | Type    | Required | Description                       |
+|-----------------|---------|----------|-----------------------------------|
+| `BUILD-TOKEN`   | string  | yes      | The build authorization token     |
+| `Content-Range` | string  | yes      | Bytes range of trace that is sent |
+
+```
+curl -X PATCH "https://gitlab.example.com/ci/api/v1/builds/1234/trace.txt" -H "BUILD-TOKEN=build_t0k3n" -H "Content-Range=0-21" -d "Running git clone...\n"
+```
+
+
+### Upload artifacts to build
+
+```
+POST /ci/api/v1/builds/:id/artifacts
+```
+
+| Attribute | Type    | Required | Description                   |
+|-----------|---------|----------|-------------------------------|
+| `id`      | integer | yes      | The ID of a build             |
+| `token`   | string  | yes      | The build authorization token |
+| `file`    | mixed   | yes      | Artifacts file                |
+
+```
+curl -X POST "https://gitlab.example.com/ci/api/v1/builds/1234/artifacts" -F "token=build_t0k3n" -F "file=@/path/to/file"
+```
+
+### Download the artifacts file from build
+
+```
+GET /ci/api/v1/builds/:id/artifacts
+```
+
+| Attribute | Type    | Required | Description                   |
+|-----------|---------|----------|-------------------------------|
+| `id`      | integer | yes      | The ID of a build             |
+| `token`   | string  | yes      | The build authorization token |
+
+```
+curl "https://gitlab.example.com/ci/api/v1/builds/1234/artifacts" -F "token=build_t0k3n"
+```
+
+### Remove the artifacts file from build
+
+```
+DELETE /ci/api/v1/builds/:id/artifacts
+```
+
+| Attribute | Type    | Required | Description                   |
+|-----------|---------|----------|-------------------------------|
+| ` id`     | integer | yes      | The ID of a build             |
+| `token`   | string  | yes      | The build authorization token |
+
+```
+curl -X DELETE "https://gitlab.example.com/ci/api/v1/builds/1234/artifacts" -F "token=build_t0k3n"
+```
diff --git a/doc/api/ci/runners.md b/doc/api/ci/runners.md
new file mode 100644
index 0000000000000000000000000000000000000000..96b3c42f773a69b8f1d4e4f9bdd6802a9ed9ca60
--- /dev/null
+++ b/doc/api/ci/runners.md
@@ -0,0 +1,57 @@
+# Runners API
+
+API used by Runners to register and delete themselves.
+
+>**Note:**
+This API is intended to be used only by Runners as their own
+communication channel. For the consumer API see the
+[new Runners API](../runners.md).
+
+## Authentication
+
+This API uses two types of authentication:
+
+1. Unique Runner's token, which is the token assigned to the Runner after it
+   has been registered.
+
+2. Using Runners' registration token.
+   This is a token that can be found in project's settings.
+   It can also be found in the **Admin > Runners** settings area.
+   There are two types of tokens you can pass: shared Runner registration
+   token or project specific registration token.
+
+## Register a new runner
+
+Used to make GitLab CI aware of available runners.
+
+```sh
+POST /ci/api/v1/runners/register
+```
+
+| Attribute | Type    | Required  | Description |
+| --------- | ------- | --------- | ----------- |
+| `token`   | string  | yes       | Runner's registration token |
+
+Example request:
+
+```sh
+curl -X POST "https://gitlab.example.com/ci/api/v1/runners/register" -F "token=t0k3n"
+```
+
+## Delete a Runner
+
+Used to remove a Runner.
+
+```sh
+DELETE /ci/api/v1/runners/delete
+```
+
+| Attribute | Type    | Required  | Description |
+| --------- | ------- | --------- | ----------- |
+| `token`   | string  | yes       | Runner's registration token |
+
+Example request:
+
+```sh
+curl -X DELETE "https://gitlab.example.com/ci/api/v1/runners/delete" -F "token=t0k3n"
+```
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 0236a7319568951d756cb86dbab67a8bb22e29a1..fccb690de92cf2a87d468df828eefd8aab44bf94 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -282,7 +282,6 @@ GET /groups/:id/members
   {
     "id": 1,
     "username": "raymond_smith",
-    "email": "ray@smith.org",
     "name": "Raymond Smith",
     "state": "active",
     "created_at": "2012-10-22T14:13:35Z",
@@ -291,7 +290,6 @@ GET /groups/:id/members
   {
     "id": 2,
     "username": "john_doe",
-    "email": "joh@doe.org",
     "name": "John Doe",
     "state": "active",
     "created_at": "2012-10-22T14:13:35Z",
diff --git a/doc/api/issues.md b/doc/api/issues.md
index fc7a7ae0c0ce4b1f11e13ee7cdc98fa75d37b0b9..0bc82ef9edb9afaacfe354bbc1f5509929b4ff3c 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -365,6 +365,9 @@ target project is not found, error `404` is returned. If the target project
 equals the source project or the user has insufficient permissions to move an
 issue, error `400` together with an explaining error message is returned.
 
+If a given label and/or milestone with the same name also exists in the target
+project, it will then be assigned to the issue that is being moved.
+
 ```
 POST /projects/:id/issues/:issue_id/move
 ```
diff --git a/doc/api/labels.md b/doc/api/labels.md
index b857d81768ef24d0c4717500cd43804b1bcd458a..a181c0f57a276486d6bf15c2967a1dfa5d5eb4ad 100644
--- a/doc/api/labels.md
+++ b/doc/api/labels.md
@@ -39,7 +39,7 @@ Example response:
    {
       "name" : "critical",
       "color" : "#d9534f",
-      "description": "Criticalissue. Need fix ASAP",
+      "description": "Critical issue. Need fix ASAP",
       "open_issues_count": 1,
       "closed_issues_count": 3,
       "open_merge_requests_count": 1
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 8217e30fe256c83e27a28806c0018cb8be5cbe0c..8dc56c15fb8da08ab40b4ac31d9f167dc32aeb8b 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -413,11 +413,13 @@ curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.c
 
 Merge changes submitted with MR using this API.
 
-If merge success you get `200 OK`.
+If the merge succeeds you'll get a `200 OK`.
 
-If it has some conflicts and can not be merged - you get 405 and error message 'Branch cannot be merged'
+If it has some conflicts and can not be merged - you'll get a 405 and the error message 'Branch cannot be merged'
 
-If merge request is already merged or closed - you get 405 and error message 'Method Not Allowed'
+If merge request is already merged or closed - you'll get a 406 and the error message 'Method Not Allowed'
+
+If the `sha` parameter is passed and does not match the HEAD of the source - you'll get a 409 and the error message 'SHA does not match HEAD of source branch'
 
 If you don't have permissions to accept this merge request - you'll get a 401
 
@@ -431,7 +433,8 @@ Parameters:
 - `merge_request_id` (required)             - ID of MR
 - `merge_commit_message` (optional)         - Custom merge commit message
 - `should_remove_source_branch` (optional)  - if `true` removes the source branch
-- `merged_when_build_succeeds` (optional)    - if `true` the MR is merge when the build succeeds
+- `merged_when_build_succeeds` (optional)   - if `true` the MR is merged when the build succeeds
+- `sha` (optional)                          - if present, then this SHA must match the HEAD of the source branch, otherwise the merge will fail
 
 ```json
 {
@@ -482,6 +485,108 @@ Parameters:
 }
 ```
 
+## Merge Request Approvals
+
+>**Note:** This API endpoint is only available on 8.9 EE and above.
+
+You can request information about a merge request's approval status using the
+following endpoint:
+
+```
+GET /projects/:id/merge_requests/:merge_request_id/approvals
+```
+
+**Parameters:**
+
+| Attribute          | Type    | Required | Description         |
+|--------------------|---------|----------|---------------------|
+| `id`               | integer | yes      | The ID of a project |
+| `merge_request_id` | integer | yes      | The ID of MR        |
+
+```json
+{
+  "id": 5,
+  "iid": 5,
+  "project_id": 1,
+  "title": "Approvals API",
+  "description": "Test",
+  "state": "opened",
+  "created_at": "2016-06-08T00:19:52.638Z",
+  "updated_at": "2016-06-08T21:20:42.470Z",
+  "merge_status": "can_be_merged",
+  "approvals_required": 2,
+  "approvals_missing": 1,
+  "approved_by": [
+    {
+      "user": {
+        "name": "Administrator",
+        "username": "root",
+        "id": 1,
+        "state": "active",
+        "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
+        "web_url": "http://localhost:3000/u/root"
+      }
+    }
+  ]
+}
+```
+
+## Approve Merge Request
+
+>**Note:** This API endpoint is only available on 8.9 EE and above.
+
+If you are allowed to, you can approve a merge request using the following 
+endpoint:
+
+```
+POST /projects/:id/merge_requests/:merge_request_id/approvals
+```
+
+**Parameters:**
+
+| Attribute          | Type    | Required | Description         |
+|--------------------|---------|----------|---------------------|
+| `id`               | integer | yes      | The ID of a project |
+| `merge_request_id` | integer | yes      | The ID of MR        |
+
+```json
+{
+  "id": 5,
+  "iid": 5,
+  "project_id": 1,
+  "title": "Approvals API",
+  "description": "Test",
+  "state": "opened",
+  "created_at": "2016-06-08T00:19:52.638Z",
+  "updated_at": "2016-06-09T21:32:14.105Z",
+  "merge_status": "can_be_merged",
+  "approvals_required": 2,
+  "approvals_missing": 0,
+  "approved_by": [
+    {
+      "user": {
+        "name": "Administrator",
+        "username": "root",
+        "id": 1,
+        "state": "active",
+        "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
+        "web_url": "http://localhost:3000/u/root"
+      }
+    },
+    {
+      "user": {
+        "name": "Nico Cartwright",
+        "username": "ryley",
+        "id": 2,
+        "state": "active",
+        "avatar_url": "http://www.gravatar.com/avatar/cf7ad14b34162a76d593e3affca2adca?s=80\u0026d=identicon",
+        "web_url": "http://localhost:3000/u/ryley"
+      }
+    }
+  ]
+}
+```
+
 ## Cancel Merge When Build Succeeds
 
 If successful you'll get `200 OK`.
@@ -569,7 +674,7 @@ GET /projects/:id/merge_requests/:merge_request_id/closes_issues
 curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/76/merge_requests/1/closes_issues
 ```
 
-Example response:
+Example response when the GitLab issue tracker is used:
 
 ```json
 [
@@ -615,6 +720,17 @@ Example response:
 ]
 ```
 
+Example response when an external issue tracker (e.g. JIRA) is used:
+
+```json
+[
+   {
+       "id" : "PROJECT-123",
+       "title" : "Title of this issue"
+   }
+]
+```
+
 ## Subscribe to a merge request
 
 Subscribes the authenticated user to a merge request to receive notification. If
diff --git a/doc/api/services.md b/doc/api/services.md
index 83ac7845156079c00b391a0957cf72605a682c0f..ccfc0fccb7f20d467afb7dd0966effc03e9ccc18 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -16,8 +16,8 @@ PUT /projects/:id/services/asana
 
 Parameters:
 
-- `api_key` (**required**) - User API token. User must have access to task,all comments will be attributed to this user.
-- `restrict_to_branch` (optional) - Comma-separated list of branches which will beautomatically inspected. Leave blank to include all branches.
+- `api_key` (**required**) - User API token. User must have access to task, all comments will be attributed to this user.
+- `restrict_to_branch` (optional) - Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches.
 
 ### Delete Asana service
 
@@ -503,6 +503,8 @@ Parameters:
 - `project_url` (**required**) - Project url
 - `issues_url` (**required**) - Issue url
 - `description` (optional) - Jira issue tracker
+- `username` (optional) - Jira username
+- `password` (optional) - Jira password
 
 ### Delete JIRA service
 
diff --git a/doc/api/settings.md b/doc/api/settings.md
index 1e745115dc816acf80d087ee76748cd35c034a6e..43a0fe35e42dbc5a9207998557fcf5f4d23659a9 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -37,7 +37,8 @@ Example response:
    "created_at" : "2016-01-04T15:44:55.176Z",
    "default_project_visibility" : 0,
    "gravatar_enabled" : true,
-   "sign_in_text" : null
+   "sign_in_text" : null,
+   "container_registry_token_expire_delay": 5
 }
 ```
 
@@ -64,6 +65,7 @@ PUT /application/settings
 | `restricted_signup_domains` | array of strings | no | Force people to use only corporate emails for sign-up. Default is null, meaning there is no restriction. |
 | `user_oauth_applications` | boolean | no | Allow users to register any application to use GitLab as an OAuth provider |
 | `after_sign_out_path` | string | no | Where to redirect users after logout |
+| `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes |
 
 ```bash
 curl -X PUT -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/application/settings?signup_enabled=false&default_project_visibility=1
@@ -90,6 +92,7 @@ Example response:
   "default_snippet_visibility": 0,
   "restricted_signup_domains": [],
   "user_oauth_applications": true,
-  "after_sign_out_path": ""
+  "after_sign_out_path": "",
+  "container_registry_token_expire_delay": 5
 }
 ```
diff --git a/doc/ci/README.md b/doc/ci/README.md
index 4abc45bf9bbd24b132e25d26992a55e3ba55a6bb..ef72df97ce6b714ea14e164912c85fdc0c85ef79 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -14,5 +14,5 @@
 - [Trigger builds through the API](triggers/README.md)
 - [Build artifacts](build_artifacts/README.md)
 - [User permissions](permissions/README.md)
-- [API](api/README.md)
+- [API](../../api/ci/README.md)
 - [CI services (linked docker containers)](services/README.md)
diff --git a/doc/ci/api/README.md b/doc/ci/api/README.md
index aea808007fcb21b7b2fb24fec13e8befca877b84..4ca8d92d7ccb22651698457590d745174bff52cc 100644
--- a/doc/ci/api/README.md
+++ b/doc/ci/api/README.md
@@ -1,22 +1,3 @@
 # GitLab CI API
 
-## Purpose
-
-Main purpose of GitLab CI API is to provide necessary data and context for
-GitLab CI Runners.
-
-For consumer API take a look at this [documentation](../../api/README.md) where
-you will find all relevant information.
-
-## API Prefix
-
-Current CI API prefix is `/ci/api/v1`.
-
-You need to prepend this prefix to all examples in this documentation, like:
-
-    GET /ci/api/v1/builds/:id/artifacts
-
-## Resources
-
-- [Builds](builds.md)
-- [Runners](runners.md)
+This document was moved to a [new location](../../api/ci/README.md).
diff --git a/doc/ci/api/builds.md b/doc/ci/api/builds.md
index 79761a893dabab1400a13e2413349efd5c82fc34..f5bd3181c0288484c980cfed891c77e4e006bc1b 100644
--- a/doc/ci/api/builds.md
+++ b/doc/ci/api/builds.md
@@ -1,139 +1,3 @@
 # Builds API
 
-API used by runners to receive and update builds.
-
-_**Note:** This API is intended to be used only by Runners as their own
-communication channel. For the consumer API see the
-[Builds API](../../api/builds.md)._
-
-## Authentication
-
-This API uses two types of authentication:
-
-1.   Unique runner's token
-
-     Token assigned to runner after it has been registered.
-
-2.   Using build authorization token
-
-     This is project's CI token that can be found in Continuous Integration
-     project settings.
-
-     Build authorization token can be passed as a parameter or a value of
-     `BUILD-TOKEN` header. This method are interchangeable.
-
-## Builds
-
-### Runs oldest pending build by runner
-
-```
-POST /ci/api/v1/builds/register
-```
-
-| Attribute | Type    | Required | Description         |
-|-----------|---------|----------|---------------------|
-| `token`   | string  | yes      | Unique runner token |
-
-
-```
-curl -X POST "https://gitlab.example.com/ci/api/v1/builds/register" -F "token=t0k3n"
-```
-
-### Update details of an existing build
-
-```
-PUT /ci/api/v1/builds/:id
-```
-
-| Attribute | Type    | Required | Description          |
-|-----------|---------|----------|----------------------|
-| `id`      | integer | yes      | The ID of a project  |
-| `token`   | string  | yes      | Unique runner token  |
-| `state`   | string  | no       | The state of a build |
-| `trace`   | string  | no       | The trace of a build |
-
-```
-curl -X PUT "https://gitlab.example.com/ci/api/v1/builds/1234" -F "token=t0k3n" -F "state=running" -F "trace=Running git clone...\n"
-```
-
-### Incremental build trace update
-
-Using this method you need to send trace content as a request body. You also need to provide the `Content-Range` header
-with a range of sent trace part. Note that you need to send parts in the proper order, so the begining of the part
-must start just after the end of the previous part. If you provide the wrong part, then GitLab CI API will return `416
-Range Not Satisfiable` response with a header `Range: 0-X`, where `X` is the current trace length.
-
-For example, if you receive `Range: 0-11` in the response, then your next part must contain a `Content-Range: 11-...`
-header and a trace part covered by this range.
-
-For a valid update API will return `202` response with:
-* `Build-Status: {status}` header containing current status of the build,
-* `Range: 0-{length}` header with the current trace length.
-
-```
-PATCH /ci/api/v1/builds/:id/trace.txt
-```
-
-Parameters:
-
-| Attribute | Type    | Required | Description          |
-|-----------|---------|----------|----------------------|
-| `id`      | integer | yes      | The ID of a build    |
-
-Headers:
-
-| Attribute       | Type    | Required | Description                       |
-|-----------------|---------|----------|-----------------------------------|
-| `BUILD-TOKEN`   | string  | yes      | The build authorization token     |
-| `Content-Range` | string  | yes      | Bytes range of trace that is sent |
-
-```
-curl -X PATCH "https://gitlab.example.com/ci/api/v1/builds/1234/trace.txt" -H "BUILD-TOKEN=build_t0k3n" -H "Content-Range=0-21" -d "Running git clone...\n"
-```
-
-
-### Upload artifacts to build
-
-```
-POST /ci/api/v1/builds/:id/artifacts
-```
-
-| Attribute | Type    | Required | Description                   |
-|-----------|---------|----------|-------------------------------|
-| `id`      | integer | yes      | The ID of a build             |
-| `token`   | string  | yes      | The build authorization token |
-| `file`    | mixed   | yes      | Artifacts file                |
-
-```
-curl -X POST "https://gitlab.example.com/ci/api/v1/builds/1234/artifacts" -F "token=build_t0k3n" -F "file=@/path/to/file"
-```
-
-### Download the artifacts file from build
-
-```
-GET /ci/api/v1/builds/:id/artifacts
-```
-
-| Attribute | Type    | Required | Description                   |
-|-----------|---------|----------|-------------------------------|
-| `id`      | integer | yes      | The ID of a build             |
-| `token`   | string  | yes      | The build authorization token |
-
-```
-curl "https://gitlab.example.com/ci/api/v1/builds/1234/artifacts" -F "token=build_t0k3n"
-```
-
-### Remove the artifacts file from build
-
-```
-DELETE /ci/api/v1/builds/:id/artifacts
-```
-
-| Attribute | Type    | Required | Description                   |
-|-----------|---------|----------|-------------------------------|
-| ` id`     | integer | yes      | The ID of a build             |
-| `token`   | string  | yes      | The build authorization token |
-
-```
-curl -X DELETE "https://gitlab.example.com/ci/api/v1/builds/1234/artifacts" -F "token=build_t0k3n"
-```
+This document was moved to a [new location](../../api/ci/builds.md).
diff --git a/doc/ci/api/runners.md b/doc/ci/api/runners.md
index 2f01da4bd76c46f45466c5e95a0561f9659998f5..b14ea99db76a3ec4c22cd28544df5bc0ea5b8f4e 100644
--- a/doc/ci/api/runners.md
+++ b/doc/ci/api/runners.md
@@ -1,46 +1,3 @@
 # Runners API
 
-API used by runners to register and delete themselves.
-
-_**Note:** This API is intended to be used only by Runners as their own
-communication channel. For the consumer API see the
-[new Runners API](../../api/runners.md)._
-
-## Authentication
-
-This API uses two types of authentication:
-
-1.   Unique runner's token
-
-     Token assigned to runner after it has been registered.
-
-2.   Using runners' registration token
-
-     This is a token that can be found in project's settings.
-     It can be also found in Admin area &raquo; Runners settings.
-
-     There are two types of tokens you can pass - shared runner registration
-     token or project specific registration token.
-
-## Runners
-
-### Register a new runner
-
-Used to make GitLab CI aware of available runners.
-
-    POST /ci/api/v1/runners/register
-
-Parameters:
-
-  * `token` (required) - Registration token
-
-
-### Delete a runner
-
-Used to remove runner.
-
-    DELETE /ci/api/v1/runners/delete
-
-Parameters:
-
-  * `token` (required) - Unique runner token
+This document was moved to a [new location](../../api/ci/runners.md).
diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md
index ca52a483a593ef7c4e6f72ab5d849470b62f9bf0..7f83f84645496e3037705d63680c7cac096ed5c4 100644
--- a/doc/ci/docker/using_docker_build.md
+++ b/doc/ci/docker/using_docker_build.md
@@ -4,14 +4,14 @@ GitLab CI allows you to use Docker Engine to build and test docker-based project
 
 **This also allows to you to use `docker-compose` and other docker-enabled tools.**
 
-This is one of new trends in Continuous Integration/Deployment to:
+One of the new trends in Continuous Integration/Deployment is to:
 
-1. create application image,
-1. run test against created image,
-1. push image to remote registry,
-1. deploy server from pushed image
+1. create an application image,
+1. run tests against the created image,
+1. push image to a remote registry, and
+1. deploy to a server from the pushed image.
 
-It's also useful in case when your application already has the `Dockerfile` that can be used to create and test image:
+It's also useful when your application already has the `Dockerfile` that can be used to create and test an image:
 ```bash
 $ docker build -t my-image dockerfiles/
 $ docker run my-docker-image /script/to/run/tests
@@ -19,24 +19,25 @@ $ docker tag my-image my-registry:5000/my-image
 $ docker push my-registry:5000/my-image
 ```
 
-However, this requires special configuration of GitLab Runner to enable `docker` support during build.
-**This requires running GitLab Runner in privileged mode which can be harmful when untrusted code is run.**
+This requires special configuration of GitLab Runner to enable `docker` support during builds.
 
-There are two methods to enable the use of `docker build` and `docker run` during build.
+## Runner Configuration
 
-## 1. Use shell executor
+There are three methods to enable the use of `docker build` and `docker run` during builds; each with their own tradeoffs.
+
+### Use shell executor
 
 The simplest approach is to install GitLab Runner in `shell` execution mode.
-GitLab Runner then executes build scripts as `gitlab-runner` user.
+GitLab Runner then executes build scripts as the `gitlab-runner` user.
 
 1. Install [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/#installation).
 
 1. During GitLab Runner installation select `shell` as method of executing build scripts or use command:
 
     ```bash
-    $ sudo gitlab-runner register -n \
+    $ sudo gitlab-ci-multi-runner register -n \
       --url https://gitlab.com/ci \
-      --token RUNNER_TOKEN \
+      --registration-token REGISTRATION_TOKEN \
       --executor shell
       --description "My Runner"
     ```
@@ -70,16 +71,18 @@ GitLab Runner then executes build scripts as `gitlab-runner` user.
 
 5. You can now use `docker` command and install `docker-compose` if needed.
 
-6. However, by adding `gitlab-runner` to `docker` group you are effectively granting `gitlab-runner` full root permissions.
-For more information please checkout [On Docker security: `docker` group considered harmful](https://www.andreas-jung.com/contents/on-docker-security-docker-group-considered-harmful).
+> **Note:**
+* By adding `gitlab-runner` to the `docker` group you are effectively granting `gitlab-runner` full root permissions.
+For more information please read [On Docker security: `docker` group considered harmful](https://www.andreas-jung.com/contents/on-docker-security-docker-group-considered-harmful).
 
-## 2. Use docker-in-docker executor
+### Use docker-in-docker executor
 
-The second approach is to use the special Docker image with all tools installed
+The second approach is to use the special docker-in-docker (dind)
+[Docker image](https://hub.docker.com/_/docker/) with all tools installed
 (`docker` and `docker-compose`) and run the build script in context of that
 image in privileged mode.
 
-In order to do that follow the steps:
+In order to do that, follow the steps:
 
 1. Install [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/#installation).
 
@@ -87,9 +90,9 @@ In order to do that follow the steps:
    mode:
 
     ```bash
-    sudo gitlab-runner register -n \
+    sudo gitlab-ci-multi-runner register -n \
       --url https://gitlab.com/ci \
-      --token RUNNER_TOKEN \
+      --registration-token REGISTRATION_TOKEN \
       --executor docker \
       --description "My Docker Runner" \
       --docker-image "docker:latest" \
@@ -119,11 +122,7 @@ In order to do that follow the steps:
         Insecure = false
     ```
 
-    If you want to use the Shared Runners available on your GitLab CE/EE
-    installation in order to build Docker images, then make sure that your
-    Shared Runners configuration has the `privileged` mode set to `true`.
-
-1. You can now use `docker` from build script:
+1. You can now use `docker` in the build script (note the inclusion of the `docker:dind` service):
 
     ```yaml
     image: docker:latest
@@ -141,14 +140,177 @@ In order to do that follow the steps:
       - docker run my-docker-image /script/to/run/tests
     ```
 
-1. However, by enabling `--docker-privileged` you are effectively disabling all
-   the security mechanisms of containers and exposing your host to privilege
-   escalation which can lead to container breakout.
-
-   For more information, check out the official Docker documentation on
-   [Runtime privilege and Linux capabilities][docker-cap].
+Docker-in-Docker works well, and is the recommended configuration, but it is not without its own challenges:
+* By enabling `--docker-privileged`, you are effectively disabling all of
+the security mechanisms of containers and exposing your host to privilege
+escalation which can lead to container breakout. For more information, check out the official Docker documentation on
+[Runtime privilege and Linux capabilities][docker-cap].
+* Using docker-in-docker, each build is in a clean environment without the past
+history. Concurrent builds work fine because every build gets it's own instance of docker engine so they won't conflict with each other. But this also means builds can be slower because there's no caching of layers.
+* By default, `docker:dind` uses `--storage-driver vfs` which is the slowest form
+offered.
 
 An example project using this approach can be found here: https://gitlab.com/gitlab-examples/docker.
 
+### Use Docker socket binding
+
+The third approach is to bind-mount `/var/run/docker.sock` into the container so that docker is available in the context of that image.
+
+In order to do that, follow the steps:
+
+1. Install [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/#installation).
+
+1. Register GitLab Runner from the command line to use `docker` and share `/var/run/docker.sock`:
+
+    ```bash
+    sudo gitlab-ci-multi-runner register -n \
+      --url https://gitlab.com/ci \
+      --registration-token REGISTRATION_TOKEN \
+      --executor docker \
+      --description "My Docker Runner" \
+      --docker-image "docker:latest" \
+      --docker-volumes /var/run/docker.sock:/var/run/docker.sock
+    ```
+
+    The above command will register a new Runner to use the special
+    `docker:latest` image which is provided by Docker. **Notice that it's using
+    the Docker daemon of the Runner itself, and any containers spawned by docker commands will be siblings of the Runner rather than children of the runner.** This may have complications and limitations that are unsuitable for your workflow.
+
+    The above command will create a `config.toml` entry similar to this:
+
+    ```
+    [[runners]]
+      url = "https://gitlab.com/ci"
+      token = REGISTRATION_TOKEN
+      executor = "docker"
+      [runners.docker]
+        tls_verify = false
+        image = "docker:latest"
+        privileged = false
+        disable_cache = false
+        volumes = ["/var/run/docker.sock", "/cache"]
+      [runners.cache]
+        Insecure = false
+    ```
+
+1. You can now use `docker` in the build script (note that you don't need to include the `docker:dind` service as when using the Docker in Docker executor):
+
+    ```yaml
+    image: docker:latest
+
+    before_script:
+    - docker info
+
+    build:
+      stage: build
+      script:
+      - docker build -t my-docker-image .
+      - docker run my-docker-image /script/to/run/tests
+    ```
+
+While the above method avoids using Docker in privileged mode, you should be aware of the following implications:
+* By sharing the docker daemon, you are effectively disabling all
+the security mechanisms of containers and exposing your host to privilege
+escalation which can lead to container breakout. For example, if a project
+ran `docker rm -f $(docker ps -a -q)` it would remove the GitLab Runner
+containers.
+* Concurrent builds may not work; if your tests
+create containers with specific names, they may conflict with each other.
+* Sharing files and directories from the source repo into containers may not
+work as expected since volume mounting is done in the context of the host
+machine, not the build container.
+e.g. `docker run --rm -t -i -v $(pwd)/src:/home/app/src test-image:latest run_app_tests`
+
+## Using the GitLab Container Registry
+
+> **Note:**
+This feature requires GitLab 8.8 and GitLab Runner 1.2.
+
+Once you've built a Docker image, you can push it up to the built-in [GitLab Container Registry](../../container_registry/README.md). For example, if you're using
+docker-in-docker on your runners, this is how your `.gitlab-ci.yml` could look:
+
+
+```yaml
+ build:
+   image: docker:latest
+   services:
+   - docker:dind
+   stage: build
+   script:
+     - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.example.com
+     - docker build -t registry.example.com/group/project:latest .
+     - docker push registry.example.com/group/project:latest
+```
+
+You have to use the credentials of the special `gitlab-ci-token` user with its
+password stored in `$CI_BUILD_TOKEN` in order to push to the Registry connected
+to your project. This allows you to automate building and deployment of your
+Docker images.
+
+Here's a more elaborate example that splits up the tasks into 4 pipeline stages,
+including two tests that run in parallel. The build is stored in the container
+registry and used by subsequent stages, downloading the image
+when needed. Changes to `master` also get tagged as `latest` and deployed using
+an application-specific deploy script:
+
+```yaml
+image: docker:latest
+services:
+- docker:dind
+
+stages:
+- build
+- test
+- release
+- deploy
+
+variables:
+  CONTAINER_TEST_IMAGE: registry.example.com/my-group/my-project:$CI_BUILD_REF_NAME
+  CONTAINER_RELEASE_IMAGE: registry.example.com/my-group/my-project:latest
+
+before_script:
+  - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.example.com
+
+build:
+  stage: build
+  script:
+    - docker build --pull -t $CONTAINER_TEST_IMAGE .
+    - docker push $CONTAINER_TEST_IMAGE
+
+test1:
+  stage: test
+  script:
+    - docker pull $CONTAINER_TEST_IMAGE
+    - docker run $CONTAINER_TEST_IMAGE /script/to/run/tests
+
+test2:
+  stage: test
+  script:
+    - docker pull $CONTAINER_TEST_IMAGE
+    - docker run $CONTAINER_TEST_IMAGE /script/to/run/another/test
+
+release-image:
+  stage: release
+  script:
+    - docker pull $CONTAINER_TEST_IMAGE
+    - docker tag $CONTAINER_TEST_IMAGE $CONTAINER_RELEASE_IMAGE
+    - docker push $CONTAINER_RELEASE_IMAGE
+  only:
+    - master
+
+deploy:
+  stage: deploy
+  script:
+    - ./deploy.sh
+  only:
+    - master
+```
+
+Some things you should be aware of when using the Container Registry:
+* You must log in to the container registry before running commands. Putting this in `before_script` will run it before each build job.
+* Using `docker build --pull` makes sure that Docker fetches any changes to base images before building just in case your cache is stale. It takes slightly longer, but means you don’t get stuck without security patches to base images.
+* Doing an explicit `docker pull` before each `docker run` makes sure to fetch the latest image that was just built. This is especially important if you are using multiple runners that cache images locally. Using the git SHA in your image tag makes this less necessary since each build will be unique and you shouldn't ever have a stale image, but it's still possible if you re-build a given commit after a dependency has changed.
+* You don't want to build directly to `latest` in case there are multiple builds happening simultaneously.
+
 [docker-in-docker]: https://blog.docker.com/2013/09/docker-can-now-run-within-docker/
 [docker-cap]: https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index 56ac2195c49cc4a39b52913ccae514d3884f9d30..a849905ac6b4f6a4341d8ba7f51fdca89a2dc544 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -23,7 +23,7 @@ To use GitLab Runner with docker you need to register a new runner to use the
 `docker` executor:
 
 ```bash
-gitlab-runner register \
+gitlab-ci-multi-runner register \
   --url "https://gitlab.com/" \
   --registration-token "PROJECT_REGISTRATION_TOKEN" \
   --description "docker-ruby-2.1" \
diff --git a/doc/ci/examples/php.md b/doc/ci/examples/php.md
index 2695301450245de9155cf1e4242f59ec2802d35e..17e1c64bb8af23aadb7fb51c07a97624ed17e5f9 100644
--- a/doc/ci/examples/php.md
+++ b/doc/ci/examples/php.md
@@ -263,10 +263,10 @@ terminal execute:
 
 ```bash
 # Check using docker executor
-gitlab-runner exec docker test:app
+gitlab-ci-multi-runner exec docker test:app
 
 # Check using shell executor
-gitlab-runner exec shell test:app
+gitlab-ci-multi-runner exec shell test:app
 ```
 
 ## Example project
diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md
index b42d7a62ebc9065224f3a01ae1a707126830e0b9..400784da61784aae547d92bf6322e9b96245bd1b 100644
--- a/doc/ci/runners/README.md
+++ b/doc/ci/runners/README.md
@@ -63,10 +63,10 @@ instance.
 Now simply register the runner as any runner:
 
 ```
-sudo gitlab-runner register
+sudo gitlab-ci-multi-runner register
 ```
 
-Shared runners are enabled by default as of GitLab 8.2, but can be disabled with the 
+Shared runners are enabled by default as of GitLab 8.2, but can be disabled with the
 `DISABLE SHARED RUNNERS` button. Previous versions of GitLab defaulted shared runners to
 disabled.
 
@@ -93,7 +93,7 @@ setup a specific runner for this project.
 To register the runner, run the command below and follow instructions:
 
 ```
-sudo gitlab-runner register
+sudo gitlab-ci-multi-runner register
 ```
 
 ###  Making an existing Shared Runner Specific
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 70fb81492d6c1277ce06ba4ddc37a70d17790196..137b080a8f796d68f35f39cb6bff49ecab471924 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -34,6 +34,7 @@ The `API_TOKEN` will take the Secure Variable value: `SECURE`.
 | **CI_BUILD_ID**         | all | The unique id of the current build that GitLab CI uses internally |
 | **CI_BUILD_REPO**       | all | The URL to clone the Git repository |
 | **CI_BUILD_TRIGGERED**  | 0.5 | The flag to indicate that build was [triggered] |
+| **CI_BUILD_TOKEN**      | 1.2 | Token used for authenticating with the GitLab Container Registry |
 | **CI_PROJECT_ID**       | all | The unique id of the current project that GitLab CI uses internally |
 | **CI_PROJECT_DIR**      | all | The full path where the repository is cloned and where the build is ran |
 
@@ -50,6 +51,7 @@ export CI_BUILD_TAG="1.0.0"
 export CI_BUILD_NAME="spec:other"
 export CI_BUILD_STAGE="test"
 export CI_BUILD_TRIGGERED="true"
+export CI_BUILD_TOKEN="abcde-1234ABCD5678ef"
 export CI_PROJECT_DIR="/builds/gitlab-org/gitlab-ce"
 export CI_PROJECT_ID="34"
 export CI_SERVER="yes"
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 63866d8c71c88fdec069b8e2cdb4dbb9580e4955..9c98f9c98c6461883753caf6239e2a30549173c3 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -28,8 +28,11 @@ If you want a quick introduction to GitLab CI, follow our
     - [only and except](#only-and-except)
     - [tags](#tags)
     - [when](#when)
+    - [environment](#environment)
     - [artifacts](#artifacts)
         - [artifacts:name](#artifacts-name)
+        - [artifacts:when](#artifacts-when)
+        - [artifacts:expire_in](#artifacts-expire_in)
     - [dependencies](#dependencies)
     - [before_script and after_script](#before_script-and-after_script)
 - [Hidden jobs](#hidden-jobs)
@@ -348,10 +351,11 @@ job_name:
 | allow_failure | no | Allow build to fail. Failed build doesn't contribute to commit status |
 | when          | no | Define when to run build. Can be `on_success`, `on_failure` or `always` |
 | dependencies  | no | Define other builds that a build depends on so that you can pass artifacts between them|
-| artifacts     | no | Define list build artifacts |
+| artifacts     | no | Define list of build artifacts |
 | cache         | no | Define list of files that should be cached between subsequent runs |
 | before_script | no | Override a set of commands that are executed before build |
 | after_script  | no | Override a set of commands that are executed after build |
+| environment   | no | Defines a name of environment to which deployment is done by this build |
 
 ### script
 
@@ -523,6 +527,31 @@ The above script will:
 1. Execute `cleanup_build_job` only when `build_job` fails
 2. Always execute `cleanup_job` as the last step in pipeline.
 
+### environment
+
+>**Note:**
+Introduced in GitLab v8.9.0.
+
+`environment` is used to define that job does deployment to specific environment.
+This allows to easily track all deployments to your environments straight from GitLab.
+
+If `environment` is specified and no environment under that name does exist a new one will be created automatically.
+
+The `environment` name must contain only letters, digits, '-' and '_'.
+
+---
+
+**Example configurations**
+
+```
+deploy to production:
+  stage: deploy
+  script: git push production HEAD:master
+  environment: production
+```
+
+The `deploy to production` job will be marked as doing deployment to `production` environment.
+
 ### artifacts
 
 >**Notes:**
@@ -651,6 +680,66 @@ job:
     untracked: true
 ```
 
+#### artifacts:when
+
+>**Note:**
+Introduced in GitLab 8.9 and GitLab Runner v1.3.0.
+
+`artifacts:when` is used to upload artifacts on build failure or despite the
+failure.
+
+`artifacts:when` can be set to one of the following values:
+
+1. `on_success` - upload artifacts only when build succeeds. This is the default
+1. `on_failure` - upload artifacts only when build fails
+1. `always` - upload artifacts despite the build status
+
+---
+
+**Example configurations**
+
+To upload artifacts only when build fails.
+
+```yaml
+job:
+  artifacts:
+    when: on_failure
+```
+
+#### artifacts:expire_in
+
+>**Note:**
+Introduced in GitLab 8.9 and GitLab Runner v1.3.0.
+
+`artifacts:expire_in` is used to remove uploaded artifacts after specified time.
+By default artifacts are stored on GitLab forver.
+`expire_in` allows to specify after what time the artifacts should be removed.
+The artifacts will expire counting from the moment when they are uploaded and stored on GitLab.
+
+After artifacts uploading you can use the **Keep** button on build page to keep the artifacts forever.
+
+Artifacts are removed every hour, but they are not accessible after expire date.
+
+The value of `expire_in` is a elapsed time. The example of parsable values:
+- '3 mins 4 sec'
+- '2 hrs 20 min'
+- '2h20min'
+- '6 mos 1 day'
+- '47 yrs 6 mos and 4d'
+- '3 weeks and 2 days'
+
+---
+
+**Example configurations**
+
+To expire artifacts after 1 week from the moment that they are uploaded:
+
+```yaml
+job:
+  artifacts:
+    expire_in: 1 week
+```
+
 ### dependencies
 
 >**Note:**
diff --git a/doc/container_registry/README.md b/doc/container_registry/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..1b46543449859937436e3564ee09ab965d66eae5
--- /dev/null
+++ b/doc/container_registry/README.md
@@ -0,0 +1,94 @@
+# GitLab Container Registry
+
+> **Note:**
+This feature was [introduced][ce-4040] in GitLab 8.8.
+
+> **Note:**
+This document is about the user guide. To learn how to enable GitLab Container
+Registry across your GitLab instance, visit the
+[administrator documentation](../administration/container_registry.md).
+
+With the Docker Container Registry integrated into GitLab, every project can
+have its own space to store its Docker images.
+
+You can read more about Docker Registry at https://docs.docker.com/registry/introduction/.
+
+---
+
+## Enable the Container Registry for your project
+
+1. First, ask your system administrator to enable GitLab Container Registry
+   following the [administration documentation](../administration/container_registry.md).
+   If you are using GitLab.com, this is enabled by default so you can start using
+   the Registry immediately.
+
+1. Go to your project's settings and enable the **Container Registry** feature
+   on your project. For new projects this might be enabled by default. For
+   existing projects you will have to explicitly enable it.
+
+    ![Enable Container Registry](img/project_feature.png)
+
+## Build and push images
+
+After you save your project's settings, you should see a new link in the
+sidebar called **Container Registry**. Following this link will get you to
+your project's Registry panel where you can see how to login to the Container
+Registry using your GitLab credentials.
+
+For example if the Registry's URL is `registry.example.com`, the you should be
+able to login with:
+
+```
+docker login registry.example.com
+```
+
+Building and publishing images should be a straightforward process. Just make
+sure that you are using the Registry URL with the namespace and project name
+that is hosted on GitLab:
+
+```
+docker build -t registry.example.com/group/project .
+docker push registry.example.com/group/project
+```
+
+## Use images from GitLab Container Registry
+
+To download and run a container from images hosted in GitLab Container Registry,
+use `docker run`:
+
+```
+docker run [options] registry.example.com/group/project [arguments]
+```
+
+For more information on running Docker containers, visit the
+[Docker documentation][docker-docs].
+
+## Control Container Registry from within GitLab
+
+GitLab offers a simple Container Registry management panel. Go to your project
+and click **Container Registry** in the left sidebar.
+
+This view will show you all tags in your project and will easily allow you to
+delete them.
+
+![Container Registry panel](img/container_registry.png)
+
+## Build and push images using GitLab CI
+
+> **Note:**
+This feature requires GitLab 8.8 and GitLab Runner 1.2.
+
+Make sure that your GitLab Runner is configured to allow building docker images.
+You have to check the [Using Docker Build documentation](../ci/docker/using_docker_build.md).
+Then see the CI documentation on [Using the GitLab Container Registry](../ci/docker/using_docker_build.md#using-the-gitlab-container-registry).
+
+## Limitations
+
+In order to use a container image from your private project as an `image:` in
+your `.gitlab-ci.yml`, you have to follow the
+[Using a private Docker Registry][private-docker]
+documentation. This workflow will be simplified in the future.
+
+[ce-4040]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4040
+[docker-docs]: https://docs.docker.com/engine/userguide/intro/
+[private-docker]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/configuration/advanced-configuration.md#using-a-private-docker-registry
diff --git a/doc/container_registry/img/container_registry.png b/doc/container_registry/img/container_registry.png
new file mode 100644
index 0000000000000000000000000000000000000000..e9505a73b408e29a78898014ccfc5b5f8eb80bf9
Binary files /dev/null and b/doc/container_registry/img/container_registry.png differ
diff --git a/doc/container_registry/img/project_feature.png b/doc/container_registry/img/project_feature.png
new file mode 100644
index 0000000000000000000000000000000000000000..57a73d253c0b8ca6c92c80fca13068e5e764a69d
Binary files /dev/null and b/doc/container_registry/img/project_feature.png differ
diff --git a/doc/development/README.md b/doc/development/README.md
index aa7d54c01d0977d366775fbe9d4f52711f47dc10..c5d5af438644796847424aa7a5e279f84f61bf6d 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -7,6 +7,7 @@
 - [Gotchas](gotchas.md) to avoid
 - [How to dump production data to staging](db_dump.md)
 - [Instrumentation](instrumentation.md)
+- [Licensing](licensing.md) for ensuring license compliance
 - [Migration Style Guide](migration_style_guide.md) for creating safe migrations
 - [Performance guidelines](performance.md)
 - [Rake tasks](rake_tasks.md) for development
diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md
index 8292b393757adaa08eb50b87b0f862d2993cbb32..f5d97179f8a3e61d7ac11412df93d1fd7b4e6212 100644
--- a/doc/development/doc_styleguide.md
+++ b/doc/development/doc_styleguide.md
@@ -103,14 +103,14 @@ Inside the document:
 
 - 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_`
+  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
+  `>**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
+  `>**Note:** This feature was introduced in GitLab EE 8.3`. Otherwise, leave
   this mention out
 
 ## References
@@ -141,6 +141,48 @@ Inside the document:
 
 [ruby-dl]: https://www.ruby-lang.org/en/downloads/ "Ruby download website"
 
+## Changing document location
+
+Changing a document's location is not to be taken lightly. Remember that the
+documentation is available to all installations under `help/` and not only to
+GitLab.com or http://docs.gitlab.com. Make sure this is discussed with the
+Documentation team beforehand.
+
+If you indeed need to change a document's location, do NOT remove the old
+document, but rather put a text in it that points to the new location, like:
+
+```
+This document was moved to [path/to/new_doc.md](path/to/new_doc.md).
+```
+
+where `path/to/new_doc.md` is the relative path to the root directory `doc/`.
+
+---
+
+For example, if you were to move `doc/workflow/lfs/lfs_administration.md` to
+`doc/administration/lfs.md`, then the steps would be:
+
+1. Copy `doc/workflow/lfs/lfs_administration.md` to `doc/administration/lfs.md`
+1. Replace the contents of `doc/workflow/lfs/lfs_administration.md` with:
+
+    ```
+    This document was moved to [administration/lfs.md](../../administration/lfs.md).
+    ```
+
+1. Find and replace any occurrences of the old location with the new one.
+   A quick way to find them is to use `grep`:
+
+    ```
+    grep -nR "lfs_administration.md" doc/
+    ```
+
+    The above command will search in the `doc/` directory for
+    `lfs_administration.md` recursively and will print the file and the line
+    where this file is mentioned. Note that we used just the filename
+    (`lfs_administration.md`) and not the whole the relative path
+    (`workflow/lfs/lfs_administration.md`).
+
+
 ## API
 
 Here is a list of must-have items. Use them in the exact order that appears
@@ -222,8 +264,8 @@ curl --data "name=foo" -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.
 
 #### Post data using JSON content
 
-_**Note:** In this example we create a new group. Watch carefully the single
-and double quotes._
+> **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
diff --git a/doc/development/instrumentation.md b/doc/development/instrumentation.md
index 9168c70945aafb1575e9dbe833c20cf3eead896e..6cd9b274d115a86815e55a7bc43d4c56d9b94b15 100644
--- a/doc/development/instrumentation.md
+++ b/doc/development/instrumentation.md
@@ -15,8 +15,8 @@ instrument code:
 * `instrument_instance_method`: instruments a single instance method.
 * `instrument_class_hierarchy`: given a Class this method will recursively
   instrument all sub-classes (both class and instance methods).
-* `instrument_methods`: instruments all public class methods of a Module.
-* `instrument_instance_methods`: instruments all public instance methods of a
+* `instrument_methods`: instruments all public and private class methods of a Module.
+* `instrument_instance_methods`: instruments all public and private instance methods of a
   Module.
 
 To remove the need for typing the full `Gitlab::Metrics::Instrumentation`
@@ -97,15 +97,16 @@ def #{name}(#{args_signature})
   trans = Gitlab::Metrics::Instrumentation.transaction
 
   if trans
-    start    = Time.now
-    retval   = super
-    duration = (Time.now - start) * 1000.0
+    start     = Time.now
+    cpu_start = Gitlab::Metrics::System.cpu_time
+    retval    = super
+    duration  = (Time.now - start) * 1000.0
 
     if duration >= Gitlab::Metrics.method_call_threshold
-      trans.increment(:method_duration, duration)
+      cpu_duration = Gitlab::Metrics::System.cpu_time - cpu_start
 
       trans.add_metric(Gitlab::Metrics::Instrumentation::SERIES,
-                       { duration: duration },
+                       { duration: duration, cpu_duration: cpu_duration },
                        method: #{label.inspect})
     end
 
diff --git a/doc/development/licensing.md b/doc/development/licensing.md
new file mode 100644
index 0000000000000000000000000000000000000000..8c8c7486ffff6c263006ea2173a109248e98f0e6
--- /dev/null
+++ b/doc/development/licensing.md
@@ -0,0 +1,93 @@
+# GitLab Licensing and Compatibility
+
+GitLab CE is licensed under the terms of the MIT License. GitLab EE is licensed under "The GitLab Enterprise Edition (EE) license" wherein there are more restrictions. See their respective LICENSE files ([CE][CE], [EE][EE]) for more information.
+
+## Automated Testing
+
+In order to comply with the terms the libraries we use are licensed under, we have to make sure to check new gems for compatible licenses whenever they're added. To automate this process, we use the [license_finder][license_finder] gem by Pivotal. It runs every time a new commit is pushed and verifies that all gems in the bundle use a license that doesn't conflict with the licensing of either GitLab Community Edition or GitLab Enterprise Edition.
+
+There are some limitations with the automated testing, however. CSS and JavaScript libraries, as well as any Ruby libraries not included by way of Bundler, must be verified manually and independently. Take care whenever one such library is used, as automated tests won't catch problematic licenses from them.
+
+Some gems may not include their license information in their `gemspec` file. These won't be detected by License Finder, and will have to be verified manually.
+
+### License Finder commands
+
+There are a few basic commands License Finder provides that you'll need in order to manage license detection.
+
+To verify that the checks are passing, and/or to see what dependencies are causing the checks to fail:
+
+```
+bundle exec license_finder
+```
+
+To whitelist a new license:
+
+```
+license_finder whitelist add MIT
+```
+
+To blacklist a new license:
+
+```
+license_finder blacklist add GPLv2
+```
+
+To tell License Finder about a dependency's license if it isn't auto-detected:
+
+```
+license_finder licenses add my_unknown_dependency MIT
+```
+
+For all of the above, please include `--why "Reason"` and `--who "My Name"` so the `decisions.yml` file can keep track of when, why, and who approved of a dependency.
+
+More detailed information on how the gem and its commands work is available in the [License Finder README][license_finder].
+
+## Acceptable Licenses
+
+Libraries with the following licenses are acceptable for use:
+
+- [The MIT License][MIT] (the MIT Expat License specifically): The MIT License requires that the license itself is included with all copies of the source. It is a permissive (non-copyleft) license as defined by the Open Source Initiative.
+- [LGPL][LGPL] (version 2, version 3): GPL constraints regarding modification and redistribution under the same license are not required of projects using an LGPL library, only upon modification of the LGPL-licensed library itself.
+- [Apache 2.0 License][apache-2]: A permissive license that also provides an express grant of patent rights from contributors to users.
+- [Ruby 1.8 License][ruby-1.8]: Dual-licensed under either itself or the GPLv2, defer to the Ruby License itself. Acceptable because of point 3b: "You may distribute the software in object code or binary form, provided that you do at least ONE of the following: b) accompany the distribution with the machine-readable source of the software."
+- [Ruby 1.9 License][ruby-1.9]: Dual-licensed under either itself or the BSD 2-Clause License, defer to BSD 2-Clause.
+- [BSD 2-Clause License][BSD-2-Clause]: A permissive (non-copyleft) license as defined by the Open Source Initiative.
+- [BSD 3-Clause License][BSD-3-Clause] (also known as New BSD or Modified BSD): A permissive (non-copyleft) license as defined by the Open Source Initiative
+- [ISC License][ISC] (also known as the OpenBSD License): A permissive (non-copyleft) license as defined by the Open Source Initiative.
+
+## Unacceptable Licenses
+
+Libraries with the following licenses are unacceptable for use:
+
+- [GNU GPL][GPL] (version 1, [version 2][GPLv2], [version 3][GPLv3], or any future versions): GPL-licensed libraries cannot be linked to from non-GPL projects.
+- [GNU AGPLv3][AGPLv3]: AGPL-licensed libraries cannot be linked to from non-GPL projects.
+
+## Notes
+
+Decisions regarding the GNU GPL licenses are based on information provided by [The GNU Project][GNU-GPL-FAQ], as well as [the Open Source Initiative][OSI-GPL], which both state that linking GPL libraries makes the program itself GPL.
+
+If a gem uses a license which is not listed above, open an issue and ask. If a license is not included in the "acceptable" list, operate under the assumption that it is not acceptable.
+
+Keep in mind that each license has its own restrictions (typically defined in their body text). Please make sure to comply with those restrictions at all times whenever an external library is used.
+
+Gems which are included only in the "development" or "test" groups by Bundler are exempt from license requirements, as they're not distributed for use in production.
+
+**NOTE:** This document is **not** legal advice, nor is it comprehensive. It should not be taken as such.
+
+[CE]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/LICENSE
+[EE]: https://gitlab.com/gitlab-org/gitlab-ee/blob/master/LICENSE
+[license_finder]: https://github.com/pivotal/LicenseFinder
+[MIT]: http://choosealicense.com/licenses/mit/
+[LGPL]: http://choosealicense.com/licenses/lgpl-3.0/
+[apache-2]: http://choosealicense.com/licenses/apache-2.0/
+[ruby-1.8]: https://github.com/ruby/ruby/blob/ruby_1_8_6/COPYING
+[ruby-1.9]: https://www.ruby-lang.org/en/about/license.txt
+[BSD-2-Clause]: https://opensource.org/licenses/BSD-2-Clause
+[BSD-3-Clause]: https://opensource.org/licenses/BSD-3-Clause
+[ISC]: https://opensource.org/licenses/ISC
+[GPL]: http://choosealicense.com/licenses/gpl-3.0/
+[GPLv2]: http://www.gnu.org/licenses/gpl-2.0.txt
+[GPLv3]: http://www.gnu.org/licenses/gpl-3.0.txt
+[AGPLv3]: http://choosealicense.com/licenses/agpl-3.0/
+[GNU-GPL-FAQ]: http://www.gnu.org/licenses/gpl-faq.html#IfLibraryIsGPL
+[OSI-GPL]: https://opensource.org/faq#linking-proprietary-code
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index 1c13b0945820e4decb1ebbff38ef85e95c15186e..8a7547e532281c0b6723744f1279f430ac883ea8 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -34,6 +34,15 @@ First, you need to provide information on whether the migration can be applied:
 3. online with errors on new instances while migrating
 4. offline (needs to happen without app servers to prevent db corruption)
 
+For example: 
+
+```
+# rubocop:disable all
+# Migration type: online without errors (works on previous version and new one)
+class MyMigration < ActiveRecord::Migration
+...
+```
+
 It is always preferable to have a migration run online. If you expect the migration
 to take particularly long (for instance, if it loops through all notes),
 this is valuable information to add.
@@ -48,7 +57,6 @@ be possible to downgrade in case of a vulnerability or bugs.
 In your migration, add a comment describing how the reversibility of the
 migration was tested.
 
-
 ## Removing indices
 
 If you need to remove index, please add a condition like in following example:
@@ -70,6 +78,7 @@ so:
 
 ```
 class MyMigration < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
   disable_ddl_transaction!
 
   def change
@@ -90,8 +99,11 @@ value of `10` you'd write the following:
 
 ```
 class MyMigration < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+  disable_ddl_transaction!
+  
   def up
-    add_column_with_default(:projects, :foo, :integer, 10)
+    add_column_with_default(:projects, :foo, :integer, default: 10)
   end
 
   def down
@@ -116,7 +128,7 @@ Example with Arel:
 users = Arel::Table.new(:users)
 users.group(users[:user_id]).having(users[:id].count.gt(5))
 
-#updtae other tables with this results
+#update other tables with these results
 ```
 
 Example with plain SQL and `quote_string` helper:
diff --git a/doc/development/testing.md b/doc/development/testing.md
index 33eed29ba5c4a08b8b23886947df2c76ea701be9..513457d203a4875d75d6618c48391a9765ea5290 100644
--- a/doc/development/testing.md
+++ b/doc/development/testing.md
@@ -65,7 +65,7 @@ the command line via `bundle exec teaspoon`, or via a web browser at
 - Use `context` to test branching logic.
 - Don't `describe` symbols (see [Gotchas](gotchas.md#dont-describe-symbols)).
 - Don't supply the `:each` argument to hooks since it's the default.
-- Prefer `not_to` to `to_not`.
+- Prefer `not_to` to `to_not` (_this is enforced by Rubocop_).
 - Try to match the ordering of tests to the ordering within the class.
 - Try to follow the [Four-Phase Test][four-phase-test] pattern, using newlines
   to separate phases.
diff --git a/doc/development/ui_guide.md b/doc/development/ui_guide.md
index a3e260a5f89d2f340995777877ce88c20e261f3b..5893b7c219eca832965139935be3a2162626610d 100644
--- a/doc/development/ui_guide.md
+++ b/doc/development/ui_guide.md
@@ -6,3 +6,51 @@ We created a page inside GitLab where you can check commonly used html and css e
 
 When you run GitLab instance locally - just visit http://localhost:3000/help/ui page to see UI examples 
 you can use during GitLab development.
+
+## Design repository
+
+All design files are stored in the [gitlab-design](https://gitlab.com/gitlab-org/gitlab-design) 
+repository and maintained by GitLab UX designers. 
+
+## Navigation
+
+GitLab's layout contains 2 sections: the left sidebar and the content. The left sidebar contains a static navigation menu. 
+This menu will be visible regardless of what page you visit. The left sidebar also contains the GitLab logo 
+and the current user's profile picture. The content section contains a header and the content itself.  
+The header describes the current GitLab page and what navigation is 
+available to user in this area. Depending on the area (project, group, profile setting) the header name and navigation may change. For example when user visits one of the 
+project pages the header will contain a project name and navigation for that project. When the user visits a group page it will contain a group name and navigation related to this group.
+
+### Adding new tab to header navigation
+
+We try to keep the amount of tabs in the header navigation between 5 and 10 so that it fits on a typical laptop screen. We also try not to confuse the user with too many options. Ideally each 
+tab should represent separate functionality. Everything related to the issue 
+tracker should be under the 'Issues' tab while everything related to the wiki should 
+be under 'Wiki' tab and so on and so forth.
+
+## Mobile screen size 
+
+We want GitLab to work well on small mobile screens as well. Size limitations make it is impossible to fit everything on a mobile screen. In this case it is OK to hide 
+part of the UI for smaller resolutions in favor of a better user experience. 
+However core functionality like browsing files, creating issues, writing comments, should
+be available on all resolutions.
+
+## Icons
+
+* `trash` icon for button or link that does destructive action like removing 
+information from database or file system
+* `x` icon for closing/hiding UI element. For example close modal window
+* `pencil` icon for edit button or link
+* `eye` icon for subscribe action
+* `rss` for rss/atom feed
+* `plus` for link or dropdown that lead to page where you create new object (For example new issue page)
+
+
+## Buttons
+
+* Button should contain icon or text. Exceptions should be approved by UX designer.
+* Use red button for destructive actions (not revertable). For example removing issue.
+* Use green or blue button for primary action. Primary button should be only one. 
+Do not use both green and blue button in one form. 
+* For all other cases use default white button 
+
diff --git a/doc/install/README.md b/doc/install/README.md
index 79ee751955cdadd5343670df38fe736e169b8d15..239f5f301ec209eb76170ca4ffa54b22d3ac1539 100644
--- a/doc/install/README.md
+++ b/doc/install/README.md
@@ -4,4 +4,3 @@
 - [Requirements](requirements.md)
 - [Structure](structure.md)
 - [Database MySQL](database_mysql.md)
-- [LDAP](ldap.md)
diff --git a/doc/install/installation.md b/doc/install/installation.md
index cfcc30ecb7a59f94a9bef2c676c8efc9d024dd94..ce80ee94e343f31bf159b3b99d9790daed8a261e 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -269,13 +269,9 @@ sudo usermod -aG redis git
 ### Clone the Source
 
     # Clone GitLab repository
-    sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ee.git -b 8-8-stable-ee gitlab
+    sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ee.git -b 8-9-stable-ee gitlab
 
-**Note:** You can change `8-8-stable-ee` to `master` if you want the *bleeding edge* version, but never install master on a production server!
-
-### Configure It
-
-    # Go to GitLab installation folder
+**Note:** You can change `8-9-stable-ee` to `master` if you want the *bleeding edge* version, but never install master on a production server!
 
 ### Configure It
 
@@ -401,7 +397,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 v0.7.1
+    sudo -u git -H git checkout v0.7.5
     sudo -u git -H make
 
 ### Initialize Database and Activate Advanced Features
diff --git a/doc/install/ldap.md b/doc/install/ldap.md
index 8236d61ba14d728bc723846431f45c9936c734a2..30f0c15daccf9dec7b9e7000455a7d38014f9520 100644
--- a/doc/install/ldap.md
+++ b/doc/install/ldap.md
@@ -1,60 +1,3 @@
-# Link LDAP Groups
-You can link LDAP groups with GitLab groups.
-It gives you ability to automatically add/remove users from GitLab groups based on LDAP groups membership.
+# GitLab LDAP integration
 
-How it works:
-1. We retrieve user ldap groups
-2. We find corresponding GitLab groups
-3. We add user to GitLab groups
-4. We remove user from GitLab groups if user has no membership in LDAP groups
-
-In order to use LDAP groups feature:
-
-1. Edit gitlab.yml config LDAP sections.
-2. Visit group settings -> LDAP tab
-3. Edit LDAP cn and access level for gitlab group
-4. Setup LDAP group members
-
-
-Example of LDAP section from gitlab.yml
-
-```
-  #
-  # 2. Auth settings
-  # ==========================
-
-  ## LDAP settings
-  ldap:
-    enabled: true
-    host: 'localhost'
-    base: 'ou=People,dc=gitlab,dc=local'
-    group_base: 'ou=Groups,dc=gitlab,dc=local'
-    port: 389
-    uid: 'uid'
-```
-
-
-# Test whether LDAP group functionality is configured correctly
-
-You need a non-LDAP admin user (such as the default admin@local.host), an LDAP user (e.g. Mary) and an LDAP group to which Mary belongs (e.g. Developers).
-
-1. As the admin, create a new group 'Developers' in GitLab and associate it with the Developers LDAP group at gitlab.example.com/admin/groups/developers/edit .
-2. Log in as Mary.
-3. Verify that Mary is now a member of the Developers group in GitLab.
-
-If you get an error message when logging in as Mary, double-check your `group_base` setting in `config/gitlab.yml`.
-
-
-# Debug LDAP user filter with ldapsearch
-
-This example uses [ldapsearch](http://www.openldap.org/software/man.cgi?query=ldapsearch&apropos=0&sektion=0&manpath=OpenLDAP+2.0-Release&format=html) and assumes you are using ActiveDirectory.
-
-The following query returns the login names of the users that will be allowed to log in to GitLab if you configure your own `user_filter`.
-
-```bash
-ldapsearch -H ldaps://$host:$port -D "$bind_dn" -y bind_dn_password.txt  -b "$base" "(&(ObjectClass=User)($user_filter))" sAMAccountName
-```
-
-- `$var` refers to a variable from the `ldap` section of your `config/gitlab.yml` https://gitlab.com/gitlab-org/gitlab-ee/blob/master/config/gitlab.yml.example#L100;
-- Replace `ldaps://` with `ldap://` if you are using the `plain` authentication method;
-- We are assuming the password for the `bind_dn` user is in `bind_dn_password.txt`.
+This document was moved under [`administration/auth/ldap`](../administration/auth/ldap.md).
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index df8e8bdc476b9025f956199a1a55052dc7015aec..09c6211b3ab80be68af2d28a037ca65bef6d2c8b 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -81,7 +81,7 @@ errors during usage.
 - More users? Run it on [multiple application servers](https://about.gitlab.com/high-availability/)
 
 We recommend having at least 1GB of swap on your server, even if you currently have
-enough available RAM. Having swap will help reduce the chance of errors occuring
+enough available RAM. Having swap will help reduce the chance of errors occurring
 if your available memory changes.
 
 Notice: The 25 workers of Sidekiq will show up as separate processes in your process overview (such as top or htop) but they share the same RAM allocation since Sidekiq is a multithreaded application. Please see the section below about Unicorn workers for information about many you need of those.
@@ -150,3 +150,4 @@ On a very active server (10,000 active users) the Sidekiq process can use 1GB+ o
 - Safari 7+ (known problem: required fields in html5 do not work)
 - Opera (Latest released version)
 - Internet Explorer (IE) 11+ but please make sure that you have the `Compatibility View` mode disabled.
+- Edge (Latest stable version)
diff --git a/doc/integration/elasticsearch.md b/doc/integration/elasticsearch.md
index a0b730a24055202569caeb70bff949d62d5e14ad..8084564feabe6f56933bfcc599a58162c3ebf7b4 100644
--- a/doc/integration/elasticsearch.md
+++ b/doc/integration/elasticsearch.md
@@ -22,7 +22,7 @@ searching in:
 - snippets
 - wiki repositories
 
-Once the data is added to the database, search indexes will be updated
+Once the data is added to the database or repository, search indexes will be updated
 automatically. Elasticsearch can be installed on the same machine that GitLab
 is installed or on a separate server.
 
@@ -45,63 +45,34 @@ use the packages that are available for your OS.
 
 ## Enable Elasticsearch
 
-In order to enable Elasticsearch you need to have access to the server that
-GitLab is hosted on.
+In order to enable Elasticsearch you need to have access to the server that GitLab is hosted on, and an administrator account on your GitLab instance. Go to **Admin > Settings** and find the "Elasticsearch" section.
 
-The following three parameters are needed to enable Elasticsearch:
+The following Elasticsearch settings are available:
 
-| Parameter | Description |
-| --------- | ----------- |
-| `enabled` | Enables/disables the Elasticsearch integration. Can be either `true` or `false` |
-| `host`    | The host where Elasticsearch is installed on. Can be either an IP or a domain name which correctly resolves to an IP. It can be changed in the [Elasticsearch configuration settings][elastic-settings]. The default value is `localhost` |
-| `port`    | The TCP port that Elasticsearch listens to. It can be changed in the [Elasticsearch configuration settings][elastic-settings]. The default value is `9200`  |
+| Parameter                           | Description |
+| ---------                           | ----------- |
+| `Elasticsearch indexing`            | Enables/disables Elasticsearch indexing. You may want to enable indexing but disable search in order to give the index time to be fully completed, for example. |
+| `Search with Elasticsearch enabled` | Enables/disables using Elasticsearch in search. |
+| `Host`                              | The TCP/IP host to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., "host1, host2"). |
+| `Port`                              | The TCP port that Elasticsearch listens to. The default value is 9200  |
 
-### Enable Elasticsearch in Omnibus installations
 
-If you have used one of the [Omnibus packages][pkg] to install GitLab, all
-you have to do is edit `/etc/gitlab/gitlab.rb` and add the following lines:
-
-```ruby
-gitlab_rails['elasticsearch_enabled'] = true
-gitlab_rails['elasticsearch_host'] = "localhost"
-gitlab_rails['elasticsearch_port'] = 9200
-```
-
-Replace the values as you see fit according to the
-[settings table above](#enable-elasticsearch).
-
-Save the file and reconfigure GitLab for the changes to take effect:
-`sudo gitlab-ctl reconfigure`.
-
-As a last step, move on to
-[add GitLab's data to the Elasticsearch index](#add-gitlabs-data-to-the-elasticsearch-index).
-
-### Enable Elasticsearch in source installations
-
-If you have installed GitLab from source, edit `/home/git/gitlab/config/gitlab.yml`:
+## Add GitLab's data to the Elasticsearch index
 
-```yaml
-elasticsearch:
-  enabled: true
-  host: localhost
-  port: 9200
-```
+Configure Elasticsearch's host and port in **Admin > Settings**. Then create empty indexes using one of the following commands:
 
-Replace the values as you see fit according to the
-[settings table above](#enable-elasticsearch).
 
-Save the file and restart GitLab for the changes to take effect:
-`sudo service gitlab restart`.
+    ```
+    # Omnibus installations
+    sudo gitlab-rake gitlab:elastic:create_empty_indexes
 
-As a last step, move on to
-[add GitLab's data to the Elasticsearch index](#add-gitlabs-data-to-the-elasticsearch-index).
+    # Installations from source
+    bundle exec rake gitlab:elastic:create_empty_indexes
+    ```
 
-## Add GitLab's data to the Elasticsearch index
 
-After [enabling Elasticsearch](#enable-elasticsearch), you must run the
-following rake tasks to add GitLab's data to the Elasticsearch index.
 
-It might take a while depending on how big your Git repositories are (see
+Then enable Elasticsearch indexing and run indexing tasks. It might take a while depending on how big your Git repositories are (see
 [Indexing large repositories](#indexing-large-repositories)).
 
 ---
@@ -167,7 +138,8 @@ sudo gitlab-rake gitlab:elastic:index_database
 # Installations from source
 bundle exec rake gitlab:elastic:index_database RAILS_ENV=production
 ```
-To index all available entities:
+
+Or everything at once (database records, repositories, wikis):
 
 ```
 # Omnibus installations
@@ -179,10 +151,7 @@ bundle exec rake gitlab:elastic:index RAILS_ENV=production
 
 ## Disable Elasticsearch
 
-Disabling the Elasticsearch integration is as easy as setting `enabled` to
-`false` in your GitLab settings. See [Enable Elasticsearch](#enable-elasticsearch)
-to find where those settings are and don't forget to reconfigure/restart GitLab
-for the changes to take effect.
+Disabling the Elasticsearch integration is as easy as unchecking `Search with Elasticsearch enabled` and `Elasticsearch indexing` in **Admin > Settings**.
 
 ## Special recommendations
 
@@ -241,8 +210,7 @@ time drop.
 
 To minimize downtime of the search feature we recommend the following:
 
-1. Configure Elasticsearch in `gitlab.yml`, or `gitlab.rb` for Omnibus
-   installations, but do not enable it, just set a host and port.
+1. Configure Elasticsearch in **Admin > Settings**, but do not enable it, just set a host and port.
 
 1. Create empty indexes:
 
@@ -257,7 +225,7 @@ To minimize downtime of the search feature we recommend the following:
 1. Index all repositories using the `gitlab:elastic:index_repositories` Rake
    task (see above). You'll probably want to do this in parallel.
 
-1. Enable Elasticsearch and restart GitLab. Note that once enabled the index will be updated when new data is pushed to the GitLab server.
+1. Enable Elasticsearch indexing.
 
 1. Run indexers for database (with the `UPDATE_INDEX=1` parameter), wikis, and
    repositories. By running  the repository indexer twice you will be sure that
diff --git a/doc/integration/google.md b/doc/integration/google.md
index f9a20dd840d46f202f78720f4705745afe2bb0b0..82978b68a34820a2bc114438c49d5541556b5569 100644
--- a/doc/integration/google.md
+++ b/doc/integration/google.md
@@ -11,9 +11,9 @@ To enable the Google OAuth2 OmniAuth provider you must register your application
     - Project ID: Must be unique to all Google Developer registered applications. Google provides a randomly generated Project ID by default. You can use the randomly generated ID or choose a new one.
 1. Refresh the page. You should now see your new project in the list. Click on the project.
 
-1. Select "APIs & auth" in the left menu.
+1. Select the "Google APIs" tab in the Overview.
 
-1. Select "APIs" in the submenu.
+1. Select and enable the following Google APIs - listed under "Popular APIs"
     - Enable `Contacts API`
     - Enable `Google+ API`
 
diff --git a/doc/integration/kerberos.md b/doc/integration/kerberos.md
index 1f8aacf38131188d4e80c6f1ff84e1109b7d1342..77811f73064d4bca2893ccf37095706662884871 100644
--- a/doc/integration/kerberos.md
+++ b/doc/integration/kerberos.md
@@ -1,16 +1,26 @@
 # Kerberos integration
 
 GitLab can be configured to allow your users to sign with their Kerberos credentials.
-Kerberos integration can be enabled as a regular omniauth provider, edit [gitlab.rb (omnibus-gitlab)`](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md#omniauth-google-twitter-github-login) or [gitlab.yml (source installations)](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/gitlab.yml.example) on your GitLab server and restart GitLab. You only need to specify the provider name. For example:
+Kerberos integration can be enabled as a regular omniauth provider, edit [gitlab.rb (omnibus-gitlab)](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template) or [gitlab.yml (source installations)](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/gitlab.yml.example) on your GitLab server and restart GitLab. You only need to specify the provider name. For example for GitLab omnibus add the following:
 
 ```
-{ name: 'kerberos'}
+gitlab_rails['omniauth_enabled'] = true
+gitlab_rails['omniauth_allow_single_sign_on'] = ['kerberos']
+gitlab_rails['omniauth_providers'] = [
+    {
+        "name" => "kerberos",
+        "app_id" => "YOUR APP ID",
+        "app_secret" => "YOUR APP SECRET",
+        "args" => { "access_type" => "offline", "approval_prompt" => "" }
+    }
+]
 ```
 
 NB: for source installations, make sure the `kerberos` gem group [has been installed](../install/installation.md#install-gems).
 
 You still need to configure your system for Kerberos usage, such as specifying realms. GitLab will make use of the system's Kerberos settings.
 
+The Administrative user can navigate to **Admin > Users > Example User > Identities** and attach a Kerberos account.
 Existing GitLab users can go to profile > account and attach a Kerberos account. if you want to allow users without a GitLab account to login you should enable the option `omniauth_allow_single_sign_on` in config file (default: false). Then, the first time a user signs in with Kerberos credentials, GitLab will create a new GitLab user associated with the email, which is built from the kerberos username and realm.
 User accounts will be created automatically when authentication was successful.
 
diff --git a/doc/license/README.md b/doc/license/README.md
index 5364d191465dac0ea92f6d602372595068ccae48..73e5527146aead4f8a5f6643b572e81d2c55e3d8 100644
--- a/doc/license/README.md
+++ b/doc/license/README.md
@@ -1,18 +1,42 @@
 # Installing your license
 
-To activate all GitLab Enterprise Edition functionality, you need to upload a license.
-Once you've received your license from GitLab Inc., you can upload it by signing into your GitLab instance as an admin, and navigating to **Admin Area &gt; License**.
+To activate all GitLab Enterprise Edition functionality, you need to upload a
+license. Once you've received your license from GitLab Inc., you can upload it
+by signing into your GitLab instance as an admin, and navigating to
+**Admin Area > License**.
 
 If you've received a `.gitlab-license` file, you can upload it directly.
 
-![upload.png](upload.png)
+![upload.png](img/upload.png)
 
-If you've received your license as plain text, you need to select the "Enter license key" option, copy the license and paste it into the "License key" field.
+If you've received your license as plain text, you need to select the
+"Enter license key" option, copy the license and paste it into the "License key"
+field.
 
-![enter.png](enter.png)
+![enter.png](img/enter.png)
 
-Once you've uploaded your license, all GitLab Enterprise Edition functionality will be active until the end of the license period.
+Once you've uploaded your license, all GitLab Enterprise Edition functionality
+will be active until the end of the license period.
 
-You can review the license details at any time in the License section of the Admin Area.
+You can review the license details at any time in the License section of the
+Admin Area.
 
-![details.png](details.png)
+![details.png](img/details.png)
+
+## Notification before the license expires
+
+One month before the license expires, a message informing when the expiration
+is due to will be shown to GitLab admins. Make sure that you update your license
+beforehand otherwise you will miss important features if it expires.
+
+![License expiration](img/expire_message.png)
+
+## What happens when my license expires?
+
+In case your license expires, you will not be able to push any commits to
+GitLab and creation of new issues and merge requests will be disabled.
+
+A message to inform of the locked state of GitLab will be presented to
+admins only.
+
+![No license message](img/no_license_message.png)
diff --git a/doc/license/details.png b/doc/license/img/details.png
similarity index 100%
rename from doc/license/details.png
rename to doc/license/img/details.png
diff --git a/doc/license/enter.png b/doc/license/img/enter.png
similarity index 100%
rename from doc/license/enter.png
rename to doc/license/img/enter.png
diff --git a/doc/license/img/expire_message.png b/doc/license/img/expire_message.png
new file mode 100644
index 0000000000000000000000000000000000000000..5ccad8491669415aa044fae5675fd2208dd3334b
Binary files /dev/null and b/doc/license/img/expire_message.png differ
diff --git a/doc/license/img/no_license_message.png b/doc/license/img/no_license_message.png
new file mode 100644
index 0000000000000000000000000000000000000000..051b0d85eb1e1e26cb1e98f7dfa201c80a50911e
Binary files /dev/null and b/doc/license/img/no_license_message.png differ
diff --git a/doc/license/upload.png b/doc/license/img/upload.png
similarity index 100%
rename from doc/license/upload.png
rename to doc/license/img/upload.png
diff --git a/doc/logs/logs.md b/doc/logs/logs.md
index ef5affa2ebd59cc14a9de924190d2d43b1ec817f..a2eca62d6915836c090649be67d83b28e13b9041 100644
--- a/doc/logs/logs.md
+++ b/doc/logs/logs.md
@@ -1,92 +1 @@
-## Log system
-GitLab has advanced log system so everything is logging and you can analize your instance using various system log files.
-In addition to system log files, GitLab Enterprise Edition comes with Audit Events. Find more about them [in Audit Events documentation](http://docs.gitlab.com/ee/administration/audit_events.html)
-
-System log files are typically plain text in a standard log file format. This guide talks about how to read and use these system log files.
-
-#### production.log
-This file lives in `/var/log/gitlab/gitlab-rails/production.log` for omnibus package or in `/home/git/gitlab/log/production.log` for installations from the source.
-
-This file contains information about all performed requests. You can see url and type of request, IP address and what exactly parts of code were involved to service this particular request. Also you can see all SQL request that have been performed and how much time it took.
-This task is more useful for GitLab contributors and developers. Use part of this log file when you are going to report bug.
-
-```
-Started GET "/gitlabhq/yaml_db/tree/master" for 168.111.56.1 at 2015-02-12 19:34:53 +0200
-Processing by Projects::TreeController#show as HTML
-  Parameters: {"project_id"=>"gitlabhq/yaml_db", "id"=>"master"}
-
-  ... [CUT OUT]
-
-  amespaces"."created_at" DESC, "namespaces"."id" DESC LIMIT 1  [["id", 26]]
-  CACHE (0.0ms)  SELECT  "members".* FROM "members"  WHERE "members"."source_type" = 'Project' AND "members"."type" IN ('ProjectMember') AND "members"."source_id" = $1 AND "members"."source_type" = $2 AND "members"."user_id" = 1  ORDER BY "members"."created_at" DESC, "members"."id" DESC LIMIT 1  [["source_id", 18], ["source_type", "Project"]]
-  CACHE (0.0ms)  SELECT  "members".* FROM "members"  WHERE "members"."source_type" = 'Project' AND "members".
-   (1.4ms)  SELECT COUNT(*) FROM "merge_requests"  WHERE "merge_requests"."target_project_id" = $1 AND ("merge_requests"."state" IN ('opened','reopened'))  [["target_project_id", 18]]
-  Rendered layouts/nav/_project.html.haml (28.0ms)
-  Rendered layouts/_collapse_button.html.haml (0.2ms)
-  Rendered layouts/_flash.html.haml (0.1ms)
-  Rendered layouts/_page.html.haml (32.9ms)
-Completed 200 OK in 166ms (Views: 117.4ms | ActiveRecord: 27.2ms)
-```
-In this example we can see that server processed HTTP request with url `/gitlabhq/yaml_db/tree/master` from IP 168.111.56.1 at 2015-02-12 19:34:53 +0200. Also we can see that request was processed by Projects::TreeController.
-
-#### application.log
-This file lives in `/var/log/gitlab/gitlab-rails/application.log` for omnibus package or in `/home/git/gitlab/log/application.log` for installations from the source.
-
-This log file helps you discover events happening in your instance such as user creation, project removing and so on.
-
-```
-October 06, 2014 11:56: User "Administrator" (admin@example.com) was created
-October 06, 2014 11:56: Documentcloud created a new project "Documentcloud / Underscore"
-October 06, 2014 11:56: Gitlab Org created a new project "Gitlab Org / Gitlab Ce"
-October 07, 2014 11:25: User "Claudie Hodkiewicz" (nasir_stehr@olson.co.uk)  was removed
-October 07, 2014 11:25: Project "project133" was removed
-```
-#### githost.log
-This file lives in `/var/log/gitlab/gitlab-rails/githost.log` for omnibus package or in `/home/git/gitlab/log/githost.log` for installations from the source.
-
-The GitLab has to interact with git repositories but in some rare cases something can go wrong and in this case you will know what exactly happened. This log file contains all failed requests from GitLab to git repository. In majority of cases this file will be useful for developers only.
-```
-December 03, 2014 13:20 -> ERROR -> Command failed [1]: /usr/bin/git --git-dir=/Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/gitlab-satellites/group184/gitlabhq/.git --work-tree=/Users/vsizov/gitlab-development-kit/gitlab/tmp/tests/gitlab-satellites/group184/gitlabhq merge --no-ff -mMerge branch 'feature_conflict' into 'feature' source/feature_conflict
-
-error: failed to push some refs to '/Users/vsizov/gitlab-development-kit/repositories/gitlabhq/gitlab_git.git'
-```
-
-#### sidekiq.log
-This file lives in `/var/log/gitlab/gitlab-rails/sidekiq.log` for omnibus package or in `/home/git/gitlab/log/sidekiq.log` for installations from the source.
-
-GitLab uses background jobs for processing tasks which can take a long time. All information about processing these jobs are writing down to this file.
-```
-2014-06-10T07:55:20Z 2037 TID-tm504 ERROR: /opt/bitnami/apps/discourse/htdocs/vendor/bundle/ruby/1.9.1/gems/redis-3.0.7/lib/redis/client.rb:228:in `read'
-2014-06-10T18:18:26Z 14299 TID-55uqo INFO: Booting Sidekiq 3.0.0 with redis options {:url=>"redis://localhost:6379/0", :namespace=>"sidekiq"}
-```
-
-#### gitlab-shell.log
-This file lives in `/var/log/gitlab/gitlab-shell/gitlab-shell.log` for omnibus package or in `/home/git/gitlab-shell/gitlab-shell.log` for installations from the source.
-
-gitlab-shell is using by Gitlab for executing git commands and provide ssh access to git repositories.
-
-```
-I, [2015-02-13T06:17:00.671315 #9291]  INFO -- : Adding project root/example.git at </var/opt/gitlab/git-data/repositories/root/dcdcdcdcd.git>.
-I, [2015-02-13T06:17:00.679433 #9291]  INFO -- : Moving existing hooks directory and simlinking global hooks directory for /var/opt/gitlab/git-data/repositories/root/example.git.
-```
-
-#### unicorn_stderr.log
-This file lives in `/var/log/gitlab/unicorn/unicorn_stderr.log` for omnibus package or in `/home/git/gitlab/log/unicorn_stderr.log` for installations from the source.
-
-Unicorn is a high-performance forking Web server which is used for serving GitLab application. You can look at this log, for example, if your application does not respond. This log cantains all information about state of unicorn processes at any given time.
-
-```
-I, [2015-02-13T06:14:46.680381 #9047]  INFO -- : Refreshing Gem list
-I, [2015-02-13T06:14:56.931002 #9047]  INFO -- : listening on addr=127.0.0.1:8080 fd=12
-I, [2015-02-13T06:14:56.931381 #9047]  INFO -- : listening on addr=/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket fd=13
-I, [2015-02-13T06:14:56.936638 #9047]  INFO -- : master process ready
-I, [2015-02-13T06:14:56.946504 #9092]  INFO -- : worker=0 spawned pid=9092
-I, [2015-02-13T06:14:56.946943 #9092]  INFO -- : worker=0 ready
-I, [2015-02-13T06:14:56.947892 #9094]  INFO -- : worker=1 spawned pid=9094
-I, [2015-02-13T06:14:56.948181 #9094]  INFO -- : worker=1 ready
-W, [2015-02-13T07:16:01.312916 #9094]  WARN -- : #<Unicorn::HttpServer:0x0000000208f618>: worker (pid: 9094) exceeds memory limit (320626688 bytes > 247066940 bytes)
-W, [2015-02-13T07:16:01.313000 #9094]  WARN -- : Unicorn::WorkerKiller send SIGQUIT (pid: 9094) alive: 3621 sec (trial 1)
-I, [2015-02-13T07:16:01.530733 #9047]  INFO -- : reaped #<Process::Status: pid 9094 exit 0> worker=1
-I, [2015-02-13T07:16:01.534501 #13379]  INFO -- : worker=1 spawned pid=13379
-I, [2015-02-13T07:16:01.534848 #13379]  INFO -- : worker=1 ready
-```
+This document was moved to [administration/logs.md](../administration/logs.md).
diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md
index 5ec0a2069b59cc58866340d9ad6409c1c8213949..8f9ef05494927a949a80446e775106b5f9e74ec9 100644
--- a/doc/migrate_ci_to_ce/README.md
+++ b/doc/migrate_ci_to_ce/README.md
@@ -355,7 +355,7 @@ sudo chown git:git /var/opt/gitlab/gitlab-ci/builds
 ```
 
 #### Problems when importing CI database to GitLab
-If you were migrating CI database from MySQL to PostgreSQL manually you can see errros during import about missing sequences:
+If you were migrating CI database from MySQL to PostgreSQL manually you can see errors during import about missing sequences:
 ```
 ALTER SEQUENCE
 ERROR:  relation "ci_builds_id_seq" does not exist
diff --git a/doc/monitoring/health_check.md b/doc/monitoring/health_check.md
index defbf37ac1903e6f1e185da7c9b02bf1047ba4f7..0d17799372f4738412f65ee15f5bb55a1369b180 100644
--- a/doc/monitoring/health_check.md
+++ b/doc/monitoring/health_check.md
@@ -11,7 +11,7 @@ and access the cache. This endpoint can be provided to uptime monitoring service
 ## Access Token
 
 An access token needs to be provided while accessing the health check endpoint. The current
-accepted token can be found on the `admin/heath_check` page of your GitLab instance.
+accepted token can be found on the `admin/health_check` page of your GitLab instance.
 
 ![access token](img/health_check_token.png)
 
diff --git a/doc/operations/moving_repositories.md b/doc/operations/moving_repositories.md
index 39086b7a2519e24eb3d22e9b5d99fcd592dd09a8..54adb99386a48f326d5e270c2f4ab23bc531a7b5 100644
--- a/doc/operations/moving_repositories.md
+++ b/doc/operations/moving_repositories.md
@@ -134,7 +134,7 @@ sudo -u git sh -c '
 cat /var/opt/gitlab/transfer-logs/* | sort | uniq -u |\
   /usr/bin/env JOBS=10 \
   /opt/gitlab/embedded/service/gitlab-rails/bin/parallel-rsync-repos \
-    /var/opt/gitlab/transfer-logs/succes-$(date +%s).log \
+    /var/opt/gitlab/transfer-logs/success-$(date +%s).log \
     /var/opt/gitlab/git-data/repositories \
     /mnt/gitlab/repositories
 '
@@ -145,7 +145,7 @@ sudo -u git -H sh -c '
 cat /home/git/transfer-logs/* | sort | uniq -u |\
   /usr/bin/env JOBS=10 \
   bin/parallel-rsync-repos \
-    /home/git/transfer-logs/succes-$(date +%s).log \
+    /home/git/transfer-logs/success-$(date +%s).log \
     /home/git/repositories \
     /mnt/gitlab/repositories
 `
@@ -164,7 +164,7 @@ sudo gitlab-rake gitlab:list_repos SINCE='2015-10-1 12:00 UTC' |\
   sudo -u git \
   /usr/bin/env JOBS=10 \
   /opt/gitlab/embedded/service/gitlab-rails/bin/parallel-rsync-repos \
-    succes-$(date +%s).log \
+    success-$(date +%s).log \
     /var/opt/gitlab/git-data/repositories \
     /mnt/gitlab/repositories
 
@@ -174,7 +174,7 @@ sudo -u git -H bundle exec rake gitlab:list_repos SINCE='2015-10-1 12:00 UTC' |\
   sudo -u git -H \
   /usr/bin/env JOBS=10 \
   bin/parallel-rsync-repos \
-    succes-$(date +%s).log \
+    success-$(date +%s).log \
     /home/git/repositories \
     /mnt/gitlab/repositories
 ```
diff --git a/doc/permissions/permissions.md b/doc/permissions/permissions.md
index 5951079b39ee94d6e10b237efdfd3cbeba1cf00c..445f267001d86154a52ed407b9bccb8019c83cd0 100644
--- a/doc/permissions/permissions.md
+++ b/doc/permissions/permissions.md
@@ -28,6 +28,7 @@ documentation](../workflow/add-user/add-user.md).
 | Manage labels                         |         | ✓          | ✓           | ✓        | ✓      |
 | See a commit status                   |         | ✓          | ✓           | ✓        | ✓      |
 | See a container registry              |         | ✓          | ✓           | ✓        | ✓      |
+| See environments                      |         | ✓          | ✓           | ✓        | ✓      |
 | Manage merge requests                 |         |            | ✓           | ✓        | ✓      |
 | Create new merge request              |         |            | ✓           | ✓        | ✓      |
 | Create new branches                   |         |            | ✓           | ✓        | ✓      |
@@ -40,6 +41,7 @@ documentation](../workflow/add-user/add-user.md).
 | Create or update commit status        |         |            | ✓           | ✓        | ✓      |
 | Update a container registry           |         |            | ✓           | ✓        | ✓      |
 | Remove a container registry image     |         |            | ✓           | ✓        | ✓      |
+| Create new environments               |         |            | ✓           | ✓        | ✓      |
 | Create new milestones                 |         |            |             | ✓        | ✓      |
 | Add new team members                  |         |            |             | ✓        | ✓      |
 | Push to protected branches            |         |            |             | ✓        | ✓      |
@@ -54,6 +56,7 @@ documentation](../workflow/add-user/add-user.md).
 | Manage variables                      |         |            |             | ✓        | ✓      |
 | Manage pages                          |         |            |             | ✓        | ✓      |
 | Manage pages domains and certificates |         |            |             | ✓        | ✓      |
+| Delete environments                   |         |            |             | ✓        | ✓      |
 | Switch visibility level               |         |            |             |          | ✓      |
 | Transfer project to another namespace |         |            |             |          | ✓      |
 | Remove project                        |         |            |             |          | ✓      |
diff --git a/doc/profile/2fa_u2f_authenticate.png b/doc/profile/2fa_u2f_authenticate.png
new file mode 100644
index 0000000000000000000000000000000000000000..b9138ff60dbf3085b0083c047541df609f69122e
Binary files /dev/null and b/doc/profile/2fa_u2f_authenticate.png differ
diff --git a/doc/profile/2fa_u2f_register.png b/doc/profile/2fa_u2f_register.png
new file mode 100644
index 0000000000000000000000000000000000000000..15b3683ef73ee59768e21e5139b43b88db56987c
Binary files /dev/null and b/doc/profile/2fa_u2f_register.png differ
diff --git a/doc/profile/two_factor_authentication.md b/doc/profile/two_factor_authentication.md
index a0e23c1586ccdd0152e4c900f071ad6f4f4e35d7..82505b13401dbce5957d2e5011d99f14bfd94130 100644
--- a/doc/profile/two_factor_authentication.md
+++ b/doc/profile/two_factor_authentication.md
@@ -8,12 +8,27 @@ your phone.
 By enabling 2FA, the only way someone other than you can log into your account
 is to know your username and password *and* have access to your phone.
 
-#### Note
+> **Note:**
 When you enable 2FA, don't forget to back up your recovery codes. For your safety, if you
 lose your codes for GitLab.com, we can't disable or recover them.  
 
+In addition to a phone application, GitLab supports U2F (universal 2nd factor) devices as
+the second factor of authentication. Once enabled, in addition to supplying your username and
+password to login, you'll be prompted to activate your U2F device (usually by pressing
+a button on it), and it will perform secure authentication on your behalf.
+
+> **Note:** Support for U2F devices was added in version 8.8
+
+The U2F workflow is only supported by Google Chrome at this point, so we _strongly_ recommend 
+that you set up both methods of two-factor authentication, so you can still access your account 
+from other browsers.
+
+> **Note:** GitLab officially only supports [Yubikey] U2F devices.
+
 ## Enabling 2FA
 
+### Enable 2FA via mobile application
+
 **In GitLab:**
 
 1. Log in to your GitLab account.
@@ -38,9 +53,26 @@ lose your codes for GitLab.com, we can't disable or recover them.
 1. Click **Submit**.
 
 If the pin you entered was correct, you'll see a message indicating that
-Two-factor Authentication has been enabled, and you'll be presented with a list
+Two-Factor Authentication has been enabled, and you'll be presented with a list
 of recovery codes.
 
+### Enable 2FA via U2F device
+
+**In GitLab:**
+
+1. Log in to your GitLab account.
+1. Go to your **Profile Settings**.
+1. Go to **Account**.
+1. Click **Enable Two-Factor Authentication**.
+1. Plug in your U2F device.
+1. Click on **Setup New U2F Device**.
+1. A light will start blinking on your device. Activate it by pressing its button.
+
+You will see a message indicating that your device was successfully set up. 
+Click on **Register U2F Device** to complete the process.
+
+![Two-Factor U2F Setup](2fa_u2f_register.png)
+
 ## Recovery Codes
 
 Should you ever lose access to your phone, you can use one of the ten provided
@@ -51,21 +83,39 @@ account.
 If you lose the recovery codes or just want to generate new ones, you can do so
 from the **Profile Settings** > **Account** page where you first enabled 2FA.
 
+> **Note:** Recovery codes are not generated for U2F devices.
+
 ## Logging in with 2FA Enabled
 
 Logging in with 2FA enabled is only slightly different than a normal login.
 Enter your username and password credentials as you normally would, and you'll
-be presented with a second prompt for an authentication code. Enter the pin from
-your phone's application or a recovery code to log in.
+be presented with a second prompt, depending on which type of 2FA you've enabled.
+
+### Log in via mobile application
+
+Enter the pin from your phone's application or a recovery code to log in.
 
-![Two-factor authentication on sign in](2fa_auth.png)
+![Two-Factor Authentication on sign in via OTP](2fa_auth.png)
+
+### Log in via U2F device
+
+1. Click **Login via U2F Device**
+1. A light will start blinking on your device. Activate it by pressing its button.
+
+You will see a message indicating that your device responded to the authentication request.
+Click on **Authenticate via U2F Device** to complete the process.
+
+![Two-Factor Authentication on sign in via U2F device](2fa_u2f_authenticate.png)
 
 ## Disabling 2FA
 
 1. Log in to your GitLab account.
 1. Go to your **Profile Settings**.
 1. Go to **Account**.
-1. Click **Disable Two-factor Authentication**.
+1. Click **Disable**, under **Two-Factor Authentication**.
+
+This will clear all your two-factor authentication registrations, including mobile
+applications and U2F devices.
 
 ## Note to GitLab administrators
 
@@ -74,3 +124,4 @@ You need to take special care to that 2FA keeps working after
 
 [Google Authenticator]: https://support.google.com/accounts/answer/1066447?hl=en
 [FreeOTP]: https://fedorahosted.org/freeotp/
+[YubiKey]: https://www.yubico.com/products/yubikey-hardware/
diff --git a/doc/update/8.6-to-8.7.md b/doc/update/8.6-to-8.7.md
index 4a2c6ea91d25570870da82c1b4386aeb7861b061..bb463d43a7c22ba88ed95bfa8cd0219e3477d916 100644
--- a/doc/update/8.6-to-8.7.md
+++ b/doc/update/8.6-to-8.7.md
@@ -86,6 +86,14 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS
 
 ### 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-6-stable:config/gitlab.yml.example origin/8-7-stable:config/gitlab.yml.example
+```
+
 #### Git configuration
 
 Disable `git gc --auto` because GitLab runs `git gc` for us already.
diff --git a/doc/update/8.7-to-8.8.md b/doc/update/8.7-to-8.8.md
index a1e01025bf53446095e3538aa54b596d04965e1a..062a8bab9dbf3fcf47790f23fa43808f26d4232c 100644
--- a/doc/update/8.7-to-8.8.md
+++ b/doc/update/8.7-to-8.8.md
@@ -86,6 +86,14 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS
 
 ### 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-7-stable:config/gitlab.yml.example origin/8-8-stable:config/gitlab.yml.example
+```
+
 #### Git configuration
 
 Disable `git gc --auto` because GitLab runs `git gc` for us already.
@@ -185,7 +193,7 @@ When index mapping is changed the whole index should be removed and built from t
 
 
 
-## Things went south? Revert to previous version (8.6)
+## Things went south? Revert to previous version (8.7)
 
 ### 1. Revert the code to the previous version
 
diff --git a/doc/update/8.8-ce-to-ee.md b/doc/update/8.8-ce-to-ee.md
index a70dfdfda035ee16eae5b48c3600a59aa2e82295..8142f3ffa86e4a5aba11dd92a9d7e311822080a3 100644
--- a/doc/update/8.8-ce-to-ee.md
+++ b/doc/update/8.8-ce-to-ee.md
@@ -64,7 +64,7 @@ To make sure you didn't miss anything run a more thorough check with:
 
 If all items are green, then congratulations upgrade complete!
 
-## Things went south? Revert to previous version (Community Edition 8.7)
+## Things went south? Revert to previous version (Community Edition 8.8)
 
 ### 1. Revert the code to the previous version
 
diff --git a/doc/update/8.8-to-8.9.md b/doc/update/8.8-to-8.9.md
new file mode 100644
index 0000000000000000000000000000000000000000..f14046bb4be5595069833c8e475375bb930078be
--- /dev/null
+++ b/doc/update/8.8-to-8.9.md
@@ -0,0 +1,162 @@
+# From 8.8 to 8.9
+
+Make sure you view this update guide from the tag (version) of GitLab you would
+like to install. In most cases this should be the highest numbered production
+tag (without rc in it). You can select the tag in the version dropdown at the
+top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
+### 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-9-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+sudo -u git -H git checkout 8-9-stable-ee
+```
+
+### 4. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+sudo -u git -H git fetch --all --tags
+sudo -u git -H git checkout v3.0.0
+```
+
+### 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 v0.7.5
+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
+
+# Optional: clean up old gems
+sudo -u git -H bundle clean
+
+# 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-8-stable:config/gitlab.yml.example origin/8-9-stable:config/gitlab.yml.example
+```
+
+#### Git configuration
+
+Disable `git gc --auto` because GitLab runs `git gc` for us already.
+
+```sh
+sudo -u git -H git config --global gc.auto 0
+```
+
+#### Nginx configuration
+
+Ensure you're still up-to-date with the latest NGINX configuration changes:
+
+```sh
+# For HTTPS configurations
+git diff origin/8-8-stable:lib/support/nginx/gitlab-ssl origin/8-9-stable:lib/support/nginx/gitlab-ssl
+
+# For HTTP configurations
+git diff origin/8-8-stable:lib/support/nginx/gitlab origin/8-9-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-9-stable/lib/support/init.d/gitlab.default.example#L37
+
+#### Init script
+
+Ensure you're still up-to-date with the latest init script changes:
+
+    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.8)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 8.7 to 8.8](8.7-to-8.8.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/8.9-ce-to-ee.md b/doc/update/8.9-ce-to-ee.md
new file mode 100644
index 0000000000000000000000000000000000000000..bd4270892fe6367805363eab970134b0130bba3f
--- /dev/null
+++ b/doc/update/8.9-ce-to-ee.md
@@ -0,0 +1,83 @@
+# From Community Edition 8.9 to Enterprise Edition 8.9
+
+This guide assumes you have a correctly configured and tested installation of
+GitLab Community Edition 8.9. If you run into any trouble or if you have any
+questions please contact us at [support@gitlab.com].
+
+### 0. Backup
+
+Make a backup just in case something goes wrong:
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+For installations using MySQL, this may require granting "LOCK TABLES"
+privileges to the GitLab user on the database version.
+
+### 1. Stop server
+
+    sudo service gitlab stop
+
+### 2. Get the EE code
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H git remote add ee https://gitlab.com/gitlab-org/gitlab-ee.git
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout 8-9-stable-ee
+```
+
+### 3. 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
+```
+
+### 4. Start application
+
+    sudo service gitlab start
+    sudo service nginx restart
+
+### 5. 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 with:
+
+    sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+
+If all items are green, then congratulations upgrade complete!
+
+## Things went south? Revert to previous version (Community Edition 8.9)
+
+### 1. Revert the code to the previous version
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H git checkout 8-7-stable
+```
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+[support@gitlab.com]: mailto:support@gitlab.com
diff --git a/doc/user/project/file_lock.md b/doc/user/project/file_lock.md
new file mode 100644
index 0000000000000000000000000000000000000000..034560d0b25c77c86516e1bdfb4844441e35d0bc
--- /dev/null
+++ b/doc/user/project/file_lock.md
@@ -0,0 +1,69 @@
+# File Lock
+
+>**Note:**
+This feature was [introduced][ee-440] in GitLab EE 8.9.
+
+---
+
+>**Note:**
+This feature needs to have a license with the "File Lock" option enabled. If
+you are using Enterprise Edition but you don't see the "Lock" button,
+ask your GitLab administrator. Check GitLab's [pricing page] for more information.
+
+GitLab gives you the ability to lock any file or folder in the repository tree
+reserving you the right to make changes to that file or folder. **Locking only
+works for the default branch you have set in the project's settings** (usually
+`master`).
+
+The file locking feature is useful in situations when:
+
+- Multiple people are working on the same file and you won't to avoid merge
+  conflicts.
+- Your repository contains binary files in which situation there is no easy
+  way to tell the diff between yours and your colleagues' changes.
+
+---
+
+If you lock a file or folder, then you are the only one who can push changes to
+the repository where the locked objects are located. Locked folders are locked
+recursively.
+
+Locks can be created by any person who has [push access] to the repository; i.e.,
+developer and higher level. Any user with master permissions can remove any
+lock, no matter who is its author.
+
+## Locking
+
+To lock a file, navigate to the repository tree under the **Code > Files** tab,
+pick the file you want to lock and hit the "Lock" button.
+
+![Locking file](img/file_lock.png)
+
+---
+
+To lock an entire folder look for the "Lock" link next to "History".
+
+![Locking folder](img/file_lock_folders.png)
+
+---
+
+After you lock a file or folder, it will appear as locked in the repository
+view.
+
+![Repository view](img/file_lock_repository_view.png)
+
+---
+
+To unlock it follow the same procedure or see the following section.
+
+## Viewing/Managing existing locks
+
+To view or manage every existing lock, navigate to the
+**Project > Code > Locked Files** section. Only the user that created the lock
+and Masters are able to remove the locked objects.
+
+![Locked Files](img/file_lock_list.png)
+
+[ee-440]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/440 "File Lock"
+[pricing page]: https://about.gitlab.com/pricing
+[push access]: ../../permissions/permissions.md
diff --git a/doc/user/project/img/file_lock.png b/doc/user/project/img/file_lock.png
new file mode 100644
index 0000000000000000000000000000000000000000..2bbaeefc67d9fb532a4b00ee440a754f465321b7
Binary files /dev/null and b/doc/user/project/img/file_lock.png differ
diff --git a/doc/user/project/img/file_lock_folders.png b/doc/user/project/img/file_lock_folders.png
new file mode 100644
index 0000000000000000000000000000000000000000..e5a0ce3f718a7c69ed86d88fb71596e441f1d420
Binary files /dev/null and b/doc/user/project/img/file_lock_folders.png differ
diff --git a/doc/user/project/img/file_lock_list.png b/doc/user/project/img/file_lock_list.png
new file mode 100644
index 0000000000000000000000000000000000000000..9efbf8427f8562f0f4dc73f834ca7ac4a5ef4cef
Binary files /dev/null and b/doc/user/project/img/file_lock_list.png differ
diff --git a/doc/user/project/img/file_lock_repository_view.png b/doc/user/project/img/file_lock_repository_view.png
new file mode 100644
index 0000000000000000000000000000000000000000..edfa39331708eb47a57e5ecef1d0ddfda21e2692
Binary files /dev/null and b/doc/user/project/img/file_lock_repository_view.png differ
diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md
index 3ebd441d9a0bee80d4e2718411919eb212ac86f7..2b90f01268b3f9100f267ff99733d914b859d1e0 100644
--- a/doc/web_hooks/web_hooks.md
+++ b/doc/web_hooks/web_hooks.md
@@ -702,6 +702,61 @@ X-Gitlab-Event: Merge Request Hook
 }
 ```
 
+## Wiki Page events
+
+Triggered when a wiki page is created or edited.
+
+**Request Header**:
+
+```
+X-Gitlab-Event: Wiki Page Hook
+```
+
+**Request Body**:
+
+```json
+{
+  "object_kind": "wiki_page",
+  "user": {
+    "name": "Administrator",
+    "username": "root",
+    "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon"
+  },
+  "project": {
+    "name": "awesome-project",
+    "description": "This is awesome",
+    "web_url": "http://example.com/root/awesome-project",
+    "avatar_url": null,
+    "git_ssh_url": "git@example.com:root/awesome-project.git",
+    "git_http_url": "http://example.com/root/awesome-project.git",
+    "namespace": "root",
+    "visibility_level": 0,
+    "path_with_namespace": "root/awesome-project",
+    "default_branch": "master",
+    "homepage": "http://example.com/root/awesome-project",
+    "url": "git@example.com:root/awesome-project.git",
+    "ssh_url": "git@example.com:root/awesome-project.git",
+    "http_url": "http://example.com/root/awesome-project.git"
+  },
+  "wiki": {
+    "web_url": "http://example.com/root/awesome-project/wikis/home",
+    "git_ssh_url": "git@example.com:root/awesome-project.wiki.git", 
+    "git_http_url": "http://example.com/root/awesome-project.wiki.git", 
+    "path_with_namespace": "root/awesome-project.wiki", 
+    "default_branch": "master"
+  },
+  "object_attributes": {
+    "title": "Awesome",
+    "content": "awesome content goes here",
+    "format": "markdown",
+    "message": "adding an awesome page to the wiki",
+    "slug": "awesome",
+    "url": "http://example.com/root/awesome-project/wikis/awesome",
+    "action": "create"
+  }
+}
+```
+
 #### Example webhook receiver
 
 If you want to see GitLab's webhooks in action for testing purposes you can use
diff --git a/doc/workflow/README.md b/doc/workflow/README.md
index 437cf93ce19a6d4adb2ba2903d909cffbdbbcf5c..c89f98242b60eeb58ecc16f18b537de6c29e50a2 100644
--- a/doc/workflow/README.md
+++ b/doc/workflow/README.md
@@ -8,6 +8,7 @@
 - [Importing to GitLab](doc/importing/README.md)
 - [Keyboard shortcuts](shortcuts.md)
 - [File finder](file_finder.md)
+- [File lock](../user/project/file_lock.md)
 - [Labels](labels.md)
 - [Issue weight](issue_weight.md)
 - [Manage large binaries with git annex](git_annex.md)
diff --git a/doc/workflow/merge_request_approvals.md b/doc/workflow/merge_request_approvals.md
index f77886e01158dbaf5bffa8d75d6704c2d155abdf..c6861659a6c5266b4706bdd6d1a070a466bbcdbe 100644
--- a/doc/workflow/merge_request_approvals.md
+++ b/doc/workflow/merge_request_approvals.md
@@ -33,7 +33,7 @@ independent of changes to the merge request.
 ### Approvers
 
 At approvers you can define the default set of users that need to approve a
-merge request.
+merge request. The author of a merge request cannot approve that merge request.
 
 If there are more approvers than required approvals, any subset of these users
 can approve the merge request.
@@ -63,4 +63,4 @@ the process is still enforced.
 
 To approve a merge request, simply press the button.
 
-![Merge request approval](merge_request_approvals/2_approvals.png)
\ No newline at end of file
+![Merge request approval](merge_request_approvals/2_approvals.png)
diff --git a/doc/workflow/merge_requests.md b/doc/workflow/merge_requests.md
index 1b5718c91c1e78ce53c306153bdd9d094c01f5b1..d2ec56e650456cf173004b3ceacce8d0d8bfcddc 100644
--- a/doc/workflow/merge_requests.md
+++ b/doc/workflow/merge_requests.md
@@ -2,6 +2,17 @@
 
 Merge requests allow you to exchange changes you made to source code
 
+## Only allow merge requests to be merged if the build succeeds
+
+You can prevent merge requests from being merged if their build did not succeed
+in the project settings page.
+
+![only_allow_merge_if_build_succeeds](merge_requests/only_allow_merge_if_build_succeeds.png)
+
+Navigate to project settings page and select the `Only allow merge requests to be merged if the build succeeds` check box.
+
+Please note that you need to have builds configured to enable this feature.
+
 ## Checkout merge requests locally
 
 Locate the section for your GitLab remote in the `.git/config` file. It looks like this:
diff --git a/doc/workflow/merge_requests/only_allow_merge_if_build_succeeds.png b/doc/workflow/merge_requests/only_allow_merge_if_build_succeeds.png
new file mode 100644
index 0000000000000000000000000000000000000000..18bebf5fe9269fdb393b8c7bd1a548b57a876707
Binary files /dev/null and b/doc/workflow/merge_requests/only_allow_merge_if_build_succeeds.png differ
diff --git a/features/admin/active_tab.feature b/features/admin/active_tab.feature
index 5de07e90e28482d45e680d51144c5ed3b48d3cdc..f5bb06dea7d76f8c9ca42edd220b6428fbdf1510 100644
--- a/features/admin/active_tab.feature
+++ b/features/admin/active_tab.feature
@@ -5,28 +5,36 @@ Feature: Admin Active Tab
 
   Scenario: On Admin Home
     Given I visit admin page
-    Then the active main tab should be Home
+    Then the active main tab should be Overview
     And no other main tabs should be active
 
   Scenario: On Admin Projects
     Given I visit admin projects page
-    Then the active main tab should be Projects
+    Then the active main tab should be Overview
+    And the active sub tab should be Projects
     And no other main tabs should be active
+    And no other sub tabs should be active
 
   Scenario: On Admin Groups
     Given I visit admin groups page
-    Then the active main tab should be Groups
+    Then the active main tab should be Overview
+    And the active sub tab should be Groups
     And no other main tabs should be active
+    And no other sub tabs should be active
 
   Scenario: On Admin Users
     Given I visit admin users page
-    Then the active main tab should be Users
+    Then the active main tab should be Overview
+    And the active sub tab should be Users
     And no other main tabs should be active
+    And no other sub tabs should be active
 
   Scenario: On Admin Logs
     Given I visit admin logs page
-    Then the active main tab should be Logs
+    Then the active main tab should be Monitoring
+    And the active sub tab should be Logs
     And no other main tabs should be active
+    And no other sub tabs should be active
 
   Scenario: On Admin Messages
     Given I visit admin messages page
@@ -40,5 +48,7 @@ Feature: Admin Active Tab
 
   Scenario: On Admin Resque
     Given I visit admin Resque page
-    Then the active main tab should be Resque
+    Then the active main tab should be Monitoring
+    And the active sub tab should be Resque
     And no other main tabs should be active
+    And no other sub tabs should be active
diff --git a/features/project/active_tab.feature b/features/project/active_tab.feature
index 0db212da54eeba402c30654e598bb8888ed0b675..af677aea2ca3cfc53d11a1b99f47fd0fdd1d23eb 100644
--- a/features/project/active_tab.feature
+++ b/features/project/active_tab.feature
@@ -10,14 +10,9 @@ Feature: Project Active Tab
     Then the active main tab should be Home
     And no other main tabs should be active
 
-  Scenario: On Project Files
+  Scenario: On Project Code
     Given I visit my project's files page
-    Then the active main tab should be Files
-    And no other main tabs should be active
-
-  Scenario: On Project Commits
-    Given I visit my project's commits page
-    Then the active main tab should be Commits
+    Then the active main tab should be Code
     And no other main tabs should be active
 
   Scenario: On Project Issues
@@ -30,11 +25,6 @@ Feature: Project Active Tab
     Then the active main tab should be Merge Requests
     And no other main tabs should be active
 
-  Scenario: On Project Members
-    Given I visit my project's members page
-    Then the active main tab should be Members
-    And no other main tabs should be active
-
   Scenario: On Project Wiki
     Given I visit my project's wiki page
     Then the active main tab should be Wiki
@@ -49,13 +39,6 @@ Feature: Project Active Tab
 
   # Sub Tabs: Settings
 
-  Scenario: On Project Settings/Edit
-    Given I visit my project's settings page
-    And I click the "Edit" tab
-    Then the active sub nav should be Edit
-    And no other sub navs should be active
-    And the active main tab should be Settings
-
   Scenario: On Project Settings/Hooks
     Given I visit my project's settings page
     And I click the "Hooks" tab
@@ -74,43 +57,53 @@ Feature: Project Active Tab
     Given I visit my project's settings page
     And I click the "Pages" tab
     Then the active sub nav should be Pages
+  
+  Scenario: On Project Members
+    Given I visit my project's members page
+    Then the active sub nav should be Members
     And no other sub navs should be active
     And the active main tab should be Settings
 
-  # Sub Tabs: Commits
+  # Sub Tabs: Code
+
+  Scenario: On Project Code/Files
+    Given I visit my project's files page
+    Then the active sub tab should be Files
+    And no other sub tabs should be active
+    And the active main tab should be Code
 
-  Scenario: On Project Commits/Commits
+  Scenario: On Project Code/Commits
     Given I visit my project's commits page
     Then the active sub tab should be Commits
     And no other sub tabs should be active
-    And the active main tab should be Commits
+    And the active main tab should be Code
 
-  Scenario: On Project Commits/Network
+  Scenario: On Project Code/Network
     Given I visit my project's network page
     Then the active sub tab should be Network
     And no other sub tabs should be active
-    And the active main tab should be Commits
+    And the active main tab should be Code
 
-  Scenario: On Project Commits/Compare
+  Scenario: On Project Code/Compare
     Given I visit my project's commits page
     And I click the "Compare" tab
     Then the active sub tab should be Compare
     And no other sub tabs should be active
-    And the active main tab should be Commits
+    And the active main tab should be Code
 
-  Scenario: On Project Commits/Branches
+  Scenario: On Project Code/Branches
     Given I visit my project's commits page
     And I click the "Branches" tab
     Then the active sub tab should be Branches
     And no other sub tabs should be active
-    And the active main tab should be Commits
+    And the active main tab should be Code
 
-  Scenario: On Project Commits/Tags
+  Scenario: On Project Code/Tags
     Given I visit my project's commits page
     And I click the "Tags" tab
     Then the active sub tab should be Tags
     And no other sub tabs should be active
-    And the active main tab should be Commits
+    And the active main tab should be Code
 
   Scenario: On Project Issues/Browse
     Given I visit my project's issues page
@@ -119,12 +112,16 @@ Feature: Project Active Tab
 
   Scenario: On Project Issues/Milestones
     Given I visit my project's issues page
-    And I click the "Milestones" tab
-    Then the active main tab should be Milestones
+    And I click the "Milestones" sub tab
+    Then the active main tab should be Issues
+    Then the active sub tab should be Milestones
     And no other main tabs should be active
+    And no other sub tabs should be active
 
   Scenario: On Project Issues/Labels
     Given I visit my project's issues page
-    And I click the "Labels" tab
-    Then the active main tab should be Labels
+    And I click the "Labels" sub tab
+    Then the active main tab should be Issues
+    Then the active sub tab should be Labels
     And no other main tabs should be active
+    And no other sub tabs should be active
diff --git a/features/project/builds/summary.feature b/features/project/builds/summary.feature
index 3c029a973df4c96ffc812857b7cde7eb8707b5f1..550ebccf0d7c391608511a3b92cb460099b7d816 100644
--- a/features/project/builds/summary.feature
+++ b/features/project/builds/summary.feature
@@ -24,3 +24,4 @@ Feature: Project Builds Summary
     Then recent build has been erased
     And recent build summary does not have artifacts widget
     And recent build summary contains information saying that build has been erased
+    And the build count cache is updated
diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature
index e1e5ed75d40d00415c9d1a7dab41503e333bace1..fd4b02ac97d9b9e57a15200f03445ea5f4f3ca7f 100644
--- a/features/project/issues/issues.feature
+++ b/features/project/issues/issues.feature
@@ -25,13 +25,6 @@ Feature: Project Issues
   Scenario: I visit issue page
     Given I click link "Release 0.4"
     Then I should see issue "Release 0.4"
-    And I should see "1 of 2" in the sidebar
-
-  Scenario: I navigate between issues
-    Given I click link "Release 0.4"
-    Then I click link "Next" in the sidebar
-    Then I should see issue "Tweet control"
-    And I should see "2 of 2" in the sidebar
 
   @javascript
   Scenario: I filter by author
diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature
index e19fe112160cdc20ce376a7870318d8ff1c092f0..0d533afc99a23a13e7afd73b5b260d804f3d4a53 100644
--- a/features/project/merge_requests.feature
+++ b/features/project/merge_requests.feature
@@ -49,14 +49,12 @@ Feature: Project Merge Requests
   Scenario: I visit an open merge request page
     Given I click link "Bug NS-04"
     Then I should see merge request "Bug NS-04"
-    And I should see "1 of 1" in the sidebar
 
   Scenario: I visit a merged merge request page
     Given project "Shop" have "Feature NS-05" merged merge request
     And I click link "Merged"
     And I click link "Feature NS-05"
     Then I should see merge request "Feature NS-05"
-    And I should see "3 of 3" in the sidebar
 
   Scenario: I close merge request page
     Given I click link "Bug NS-04"
@@ -76,18 +74,6 @@ Feature: Project Merge Requests
     And I submit new merge request "Wiki Feature"
     Then I should see merge request "Wiki Feature"
 
-  Scenario: I download a diff on a public merge request
-    Given public project "Community"
-    And "John Doe" owns public project "Community"
-    And project "Community" has "Bug CO-01" open merge request with diffs inside
-    Given I logout directly
-    And I visit merge request page "Bug CO-01"
-    And I click on "Email Patches"
-    Then I should see a patch diff
-    And I visit merge request page "Bug CO-01"
-    And I click on "Plain Diff"
-    Then I should see a patch diff
-
   @javascript
   Scenario: I comment on a merge request
     Given I visit merge request page "Bug NS-04"
@@ -341,13 +327,6 @@ Feature: Project Merge Requests
     And I click link "Close"
     Then I should see closed merge request "Bug NS-04"
 
-  Scenario: I approve merge request
-    Given merge request 'Bug NS-04' must be approved
-    And I click link "Bug NS-04"
-    And I should not see merge button
-    When I click link "Approve"
-    Then I should see approved merge request "Bug NS-04"
-
   Scenario: Reporter can approve merge request
     Given I am a "Shop" reporter
     And I visit project "Shop" merge requests page
@@ -357,13 +336,12 @@ Feature: Project Merge Requests
     When I click link "Approve"
     Then I should see message that merge request can be merged
 
-  Scenario: I approve merge request if I am an approver
-    Given merge request 'Bug NS-04' must be approved by current user
+  Scenario: I can not approve Merge request if I am the author
+    Given merge request 'Bug NS-04' must be approved
     And I click link "Bug NS-04"
     And I should not see merge button
-    And I should see message that MR require an approval from me
-    When I click link "Approve"
-    Then I should see approved merge request "Bug NS-04"
+    When I should not see Approve button
+    And I should see message that MR require an approval
 
   Scenario: I can not approve merge request if I am not an approver
     Given merge request 'Bug NS-04' must be approved by some user
diff --git a/features/project/project.feature b/features/project/project.feature
index 8064d9c9156b8326eacb34632446bba8b66d17c5..6453671463eec805706ae2a7ec8f10b99ab44788 100644
--- a/features/project/project.feature
+++ b/features/project/project.feature
@@ -30,15 +30,6 @@ Feature: Project
     When I enable project issues
     Then I should see the issues settings
 
-  Scenario: I should have back to group button
-    And project "Shop" belongs to group
-    And I visit project "Shop" page
-    Then I should see back to group button
-
-  Scenario: I should have back to group button
-    And I visit project "Shop" page
-    Then I should see back to dashboard button
-
   Scenario: I should have readme on page
     And I visit project "Shop" page
     Then I should see project "Shop" README
diff --git a/features/project/shortcuts.feature b/features/project/shortcuts.feature
index 10e7c23461057db7f8a9f825fec983f8b75d0b74..c73d0b323376899f2b26255e23a51439c90f965f 100644
--- a/features/project/shortcuts.feature
+++ b/features/project/shortcuts.feature
@@ -8,19 +8,21 @@ Feature: Project Shortcuts
   @javascript
   Scenario: Navigate to files tab
     Given I press "g" and "f"
-    Then the active main tab should be Files
+    Then the active main tab should be Code
+    Then the active sub tab should be Files
 
   @javascript
   Scenario: Navigate to commits tab
     Given I visit my project's files page
     Given I press "g" and "c"
-    Then the active main tab should be Commits
+    Then the active main tab should be Code
+    Then the active sub tab should be Commits
 
   @javascript
   Scenario: Navigate to network tab
     Given I press "g" and "n"
     Then the active sub tab should be Network
-    And the active main tab should be Commits
+    And the active main tab should be Code
 
   @javascript
   Scenario: Navigate to graphs tab
diff --git a/features/steps/admin/active_tab.rb b/features/steps/admin/active_tab.rb
index 90d13abdb1317356fa57fea0817d14eb44da5667..9b1689a8198212d1a00e40495f74d7b06e40ba58 100644
--- a/features/steps/admin/active_tab.rb
+++ b/features/steps/admin/active_tab.rb
@@ -3,32 +3,36 @@ class Spinach::Features::AdminActiveTab < Spinach::FeatureSteps
   include SharedPaths
   include SharedActiveTab
 
-  step 'the active main tab should be Home' do
+  step 'the active main tab should be Overview' do
     ensure_active_main_tab('Overview')
   end
 
-  step 'the active main tab should be Projects' do
-    ensure_active_main_tab('Projects')
+  step 'the active sub tab should be Projects' do
+    ensure_active_sub_tab('Projects')
   end
 
-  step 'the active main tab should be Groups' do
-    ensure_active_main_tab('Groups')
+  step 'the active sub tab should be Groups' do
+    ensure_active_sub_tab('Groups')
   end
 
-  step 'the active main tab should be Users' do
-    ensure_active_main_tab('Users')
-  end
-
-  step 'the active main tab should be Logs' do
-    ensure_active_main_tab('Logs')
+  step 'the active sub tab should be Users' do
+    ensure_active_sub_tab('Users')
   end
 
   step 'the active main tab should be Hooks' do
     ensure_active_main_tab('Hooks')
   end
 
-  step 'the active main tab should be Resque' do
-    ensure_active_main_tab('Background Jobs')
+  step 'the active main tab should be Monitoring' do
+    ensure_active_main_tab('Monitoring')
+  end
+
+  step 'the active sub tab should be Resque' do
+    ensure_active_sub_tab('Background Jobs')
+  end
+
+  step 'the active sub tab should be Logs' do
+    ensure_active_sub_tab('Logs')
   end
 
   step 'the active main tab should be Messages' do
diff --git a/features/steps/dashboard/active_tab.rb b/features/steps/dashboard/active_tab.rb
index 0e2c04fb29994cfe645c9fc31b99e13092673a6f..04fe96cef22bc61a6ee3a2d70cc48a3ded03b43c 100644
--- a/features/steps/dashboard/active_tab.rb
+++ b/features/steps/dashboard/active_tab.rb
@@ -1,9 +1,5 @@
 class Spinach::Features::DashboardActiveTab < Spinach::FeatureSteps
   include SharedAuthentication
   include SharedPaths
-  include SharedActiveTab
-
-  step 'the active main tab should be Help' do
-    ensure_active_main_tab('Help')
-  end
+  include SharedSidebarActiveTab
 end
diff --git a/features/steps/dashboard/group.rb b/features/steps/dashboard/group.rb
index 0c6a0ae37251c03f5cd5d838576eddf7b55ddcc6..9b79a3be49b3a2f373265c128c8a351e938f5e22 100644
--- a/features/steps/dashboard/group.rb
+++ b/features/steps/dashboard/group.rb
@@ -62,6 +62,6 @@ class Spinach::Features::DashboardGroup < Spinach::FeatureSteps
   end
 
   step 'I should see the "Can not leave message"' do
-    expect(page).to have_content "You can not leave Owned group because you're the last owner"
+    expect(page).to have_content "You can not leave the \"Owned\" group."
   end
 end
diff --git a/features/steps/dashboard/new_project.rb b/features/steps/dashboard/new_project.rb
index a0aad66184d1900325889ad19de831a308009eea..5308e77fb1981e69c7945f4bcc66e5b2405838df 100644
--- a/features/steps/dashboard/new_project.rb
+++ b/features/steps/dashboard/new_project.rb
@@ -10,7 +10,8 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps
   end
 
   step 'I see "New Project" page' do
-    expect(page).to have_content('Project path')
+    expect(page).to have_content('Project owner')
+    expect(page).to have_content('Project name')
   end
 
   step 'I see all possible import optios' do
diff --git a/features/steps/dashboard/shortcuts.rb b/features/steps/dashboard/shortcuts.rb
index a9083850b523fca2a0c0a0ab07ba3ab0fa82559d..118d27888df4619acc448ce847cd1b2e17d8035d 100644
--- a/features/steps/dashboard/shortcuts.rb
+++ b/features/steps/dashboard/shortcuts.rb
@@ -2,5 +2,6 @@ class Spinach::Features::DashboardShortcuts < Spinach::FeatureSteps
   include SharedAuthentication
   include SharedPaths
   include SharedProject
-  include SharedActiveTab
+  include SharedSidebarActiveTab
+  include SharedShortcuts
 end
diff --git a/features/steps/dashboard/todos.rb b/features/steps/dashboard/todos.rb
index 2b23df6764b7bf14c7d316ce2950de4b3b1984cf..19fedfbfcdf7c7cde5e39d03d38608d1e01205be 100644
--- a/features/steps/dashboard/todos.rb
+++ b/features/steps/dashboard/todos.rb
@@ -20,13 +20,12 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps
   step 'I have todos' do
     create(:todo, user: current_user, project: project, author: mary_jane, target: issue, action: Todo::MENTIONED)
     create(:todo, user: current_user, project: project, author: john_doe, target: issue, action: Todo::ASSIGNED)
-    note = create(:note, author: john_doe, noteable: issue, note: "#{current_user.to_reference} Wdyt?")
+    note = create(:note, author: john_doe, noteable: issue, note: "#{current_user.to_reference} Wdyt?", project: project)
     create(:todo, user: current_user, project: project, author: john_doe, target: issue, action: Todo::MENTIONED, note: note)
     create(:todo, user: current_user, project: project, author: john_doe, target: merge_request, action: Todo::ASSIGNED)
   end
 
   step 'I should see todos assigned to me' do
-    page.within('.nav-sidebar') { expect(page).to have_content 'Todos 4' }
     expect(page).to have_content 'To do 4'
     expect(page).to have_content 'Done 0'
 
@@ -42,7 +41,6 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps
       click_link 'Done'
     end
 
-    page.within('.nav-sidebar') { expect(page).to have_content 'Todos 3' }
     expect(page).to have_content 'To do 3'
     expect(page).to have_content 'Done 1'
     should_not_see_todo "John Doe assigned you merge request #{merge_request.to_reference}"
@@ -106,7 +104,7 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps
       if pending
         expect(page).to have_link 'Done'
       else
-        expect(page).to_not have_link 'Done'
+        expect(page).not_to have_link 'Done'
       end
     end
   end
diff --git a/features/steps/group/members.rb b/features/steps/group/members.rb
index 0706df3aec5d709e923f27d79dbd61839beb5095..dfa2fa75def097b4a4fdf0c2b62827a33c8dc810 100644
--- a/features/steps/group/members.rb
+++ b/features/steps/group/members.rb
@@ -53,7 +53,7 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
   step 'I should see "sjobs@apple.com" in team list as invited "Reporter"' do
     page.within '.content-list' do
       expect(page).to have_content('sjobs@apple.com')
-      expect(page).to have_content('invited')
+      expect(page).to have_content('Invited')
       expect(page).to have_content('Reporter')
     end
   end
@@ -116,11 +116,9 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
     member = mary_jane_member
 
     page.within "#group_member_#{member.id}" do
-      find(".js-toggle-button").click
-      page.within "#edit_group_member_#{member.id}" do
-        select 'Developer', from: 'group_member_access_level'
-        click_on 'Save'
-      end
+      click_button "Edit access level"
+      select 'Developer', from: 'group_member_access_level'
+      click_on 'Save'
     end
   end
 
@@ -128,9 +126,7 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
     member = mary_jane_member
 
     page.within "#group_member_#{member.id}" do
-      page.within '.member-access-level' do
-        expect(page).to have_content "Developer"
-      end
+      expect(page).to have_content "Developer"
     end
   end
 
diff --git a/features/steps/groups_management.rb b/features/steps/groups_management.rb
index 2511ac3073797e49f03f0089a3a5206be5c072f2..b0e37a06b629520f1989978a835692763e497635 100644
--- a/features/steps/groups_management.rb
+++ b/features/steps/groups_management.rb
@@ -35,7 +35,7 @@ class Spinach::Features::GroupsManagement < Spinach::FeatureSteps
   step 'I reload "Open" project members page' do
     visit root_path
     click_link 'Sourcing / Open'
-    page.within('.nav-sidebar') do
+    page.within('.project-settings-dropdown') do
       click_link 'Members'
     end
   end
@@ -64,6 +64,5 @@ class Spinach::Features::GroupsManagement < Spinach::FeatureSteps
   step 'I cannot control user membership from project page' do
     expect(page).not_to have_button 'Add members'
     expect(page).not_to have_link 'Import members'
-    expect(page).to have_selector '#project_member_access_level'
   end
 end
diff --git a/features/steps/profile/active_tab.rb b/features/steps/profile/active_tab.rb
index 3b59089a093e0277849370283a35cca8b2898386..4724a32627778377b4d1ec08a4a384004b138330 100644
--- a/features/steps/profile/active_tab.rb
+++ b/features/steps/profile/active_tab.rb
@@ -22,8 +22,4 @@ class Spinach::Features::ProfileActiveTab < Spinach::FeatureSteps
   step 'the active main tab should be Audit Log' do
     ensure_active_main_tab('Audit Log')
   end
-
-  def ensure_active_main_tab(content)
-    expect(find('.layout-nav li.active')).to have_content(content)
-  end
 end
diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb
index b1a87b96efd1153ee108cf3bf56932cba8807c2e..9e5602dacf1af922cc00ab81e11060cc0bbd2c1b 100644
--- a/features/steps/profile/profile.rb
+++ b/features/steps/profile/profile.rb
@@ -155,6 +155,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
   end
 
   step 'I click on my profile picture' do
+    find(:css, '.side-nav-toggle').click
     find(:css, '.sidebar-user').click
   end
 
diff --git a/features/steps/project/active_tab.rb b/features/steps/project/active_tab.rb
index 0195be6d5593cf0b87fc69a3931395cf105d1268..467afe8e77a9a61752b9e1fde79c7486da47ba51 100644
--- a/features/steps/project/active_tab.rb
+++ b/features/steps/project/active_tab.rb
@@ -16,12 +16,14 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
   end
 
   step 'I click the "Snippets" tab' do
-    click_link('Snippets')
+    page.within('.layout-nav') do
+      click_link('Snippets')
+    end
   end
 
-  step 'I click the "Edit" tab' do
-    page.within '.sidebar-subnav' do
-      click_link('Project Settings')
+  step 'I click the "Edit Project"' do
+    page.within '.layout-nav .controls' do
+      click_link('Edit Project')
     end
   end
 
@@ -37,14 +39,10 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
     click_link('Pages')
   end
 
-  step 'the active sub nav should be Team' do
+  step 'the active sub nav should be Members' do
     ensure_active_sub_nav('Members')
   end
 
-  step 'the active sub nav should be Edit' do
-    ensure_active_sub_nav('Project')
-  end
-
   step 'the active sub nav should be Hooks' do
     ensure_active_sub_nav('Webhooks')
   end
@@ -64,17 +62,15 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
   end
 
   step 'I click the "Branches" tab' do
-    click_link('Branches')
+    page.within '.content' do
+      click_link('Branches')
+    end
   end
 
   step 'I click the "Tags" tab' do
     click_link('Tags')
   end
 
-  step 'the active sub tab should be Commits' do
-    ensure_active_sub_tab('Commits')
-  end
-
   step 'the active sub tab should be Compare' do
     ensure_active_sub_tab('Compare')
   end
@@ -89,23 +85,27 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
 
   # Sub Tabs: Issues
 
-  step 'I click the "Milestones" tab' do
-    click_link('Milestones')
+  step 'I click the "Milestones" sub tab' do
+    page.within('.sub-nav') do
+      click_link('Milestones')
+    end
   end
 
-  step 'I click the "Labels" tab' do
-    click_link('Labels')
+  step 'I click the "Labels" sub tab' do
+    page.within('.sub-nav') do
+      click_link('Labels')
+    end
   end
 
   step 'the active sub tab should be Issues' do
     ensure_active_sub_tab('Issues')
   end
 
-  step 'the active main tab should be Milestones' do
-    ensure_active_main_tab('Milestones')
+  step 'the active sub tab should be Milestones' do
+    ensure_active_sub_tab('Milestones')
   end
 
-  step 'the active main tab should be Labels' do
-    ensure_active_main_tab('Labels')
+  step 'the active sub tab should be Labels' do
+    ensure_active_sub_tab('Labels')
   end
 end
diff --git a/features/steps/project/builds/artifacts.rb b/features/steps/project/builds/artifacts.rb
index 1bdb57af9d13c47fd74b8161081e9b04e300355c..2876e8812e9e319db61554f0b0361a941cf7388f 100644
--- a/features/steps/project/builds/artifacts.rb
+++ b/features/steps/project/builds/artifacts.rb
@@ -5,11 +5,11 @@ class Spinach::Features::ProjectBuildsArtifacts < Spinach::FeatureSteps
   include RepoHelpers
 
   step 'I click artifacts download button' do
-    page.within('.artifacts') { click_link 'Download' }
+    click_link 'Download'
   end
 
   step 'I click artifacts browse button' do
-    page.within('.artifacts') { click_link 'Browse' }
+    click_link 'Browse'
   end
 
   step 'I should see content of artifacts archive' do
diff --git a/features/steps/project/builds/summary.rb b/features/steps/project/builds/summary.rb
index e9e2359146e4b30870da1b66f0c982db416a8bbc..374eb0b0e07b7fa2c6e4b6a6d789654ccca1c199 100644
--- a/features/steps/project/builds/summary.rb
+++ b/features/steps/project/builds/summary.rb
@@ -36,4 +36,8 @@ class Spinach::Features::ProjectBuildsSummary < Spinach::FeatureSteps
       expect(page).to have_content 'Build has been erased'
     end
   end
+
+  step 'the build count cache is updated' do
+    expect(@build.project.running_or_pending_build_count).to eq @build.project.builds.running_or_pending.count(:all)
+  end
 end
diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb
index f33f37be95149acd91cd2cf7a90f66353992ab27..239036e431d384b7017a495616db97beea3c40cd 100644
--- a/features/steps/project/commits/commits.rb
+++ b/features/steps/project/commits/commits.rb
@@ -105,7 +105,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
   end
 
   step 'I should not see button to create a new merge request' do
-    expect(page).to_not have_link 'Create Merge Request'
+    expect(page).not_to have_link 'Create Merge Request'
   end
 
   step 'I should see button to the merge request' do
@@ -164,12 +164,12 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
 
   step 'commit has ci status' do
     @project.enable_ci
-    ci_commit = create :ci_commit, project: @project, sha: sample_commit.id
-    create :ci_build, commit: ci_commit
+    pipeline = create :ci_pipeline, project: @project, sha: sample_commit.id
+    create :ci_build, pipeline: pipeline
   end
 
   step 'repository contains ".gitlab-ci.yml" file' do
-    allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file).and_return(String.new)
+    allow_any_instance_of(Ci::Pipeline).to receive(:ci_yaml_file).and_return(String.new)
   end
 
   step 'I see commit ci info' do
diff --git a/features/steps/project/elastic/global_search.rb b/features/steps/project/elastic/global_search.rb
index 841bf14296c45b58618aa29498379bc96eff56e3..3c1da156dc26bd870de1d55a84a6b47e10052ff6 100644
--- a/features/steps/project/elastic/global_search.rb
+++ b/features/steps/project/elastic/global_search.rb
@@ -1,8 +1,11 @@
+require Rails.root.join('spec', 'support', 'stub_configuration')
+
 class Spinach::Features::GlobalSearch < Spinach::FeatureSteps
   include SharedAuthentication
   include SharedPaths
   include SharedProject
   include SharedElastic
+  include StubConfiguration
 
   before do
     [::Project, Issue, MergeRequest, Milestone].each do |model|
@@ -15,7 +18,7 @@ class Spinach::Features::GlobalSearch < Spinach::FeatureSteps
       model.__elasticsearch__.delete_index!
     end
 
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(false)
+    stub_application_setting(elasticsearch_search: false, elasticsearch_indexing: false)
   end
 
   step 'project has all data available for the search' do
diff --git a/features/steps/project/elastic/project_search.rb b/features/steps/project/elastic/project_search.rb
index cd56d7f0032c2eb5050809b4c7172f59c7d3eaf6..eeb0317ff87767c1f9159b91592f63a9d9799c77 100644
--- a/features/steps/project/elastic/project_search.rb
+++ b/features/steps/project/elastic/project_search.rb
@@ -3,6 +3,7 @@ class Spinach::Features::ProjectSearch < Spinach::FeatureSteps
   include SharedPaths
   include SharedProject
   include SharedElastic
+  include StubConfiguration
 
   before do
     [::Project, Repository, Note, MergeRequest, Milestone, ::ProjectWiki, Issue].each do |model|
@@ -15,7 +16,7 @@ class Spinach::Features::ProjectSearch < Spinach::FeatureSteps
       model.__elasticsearch__.delete_index!
     end
 
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(false)
+    stub_application_setting(elasticsearch_search: false, elasticsearch_indexing: false)
   end
 
   step 'project has all data available for the search' do
diff --git a/features/steps/project/elastic/snippets_search.rb b/features/steps/project/elastic/snippets_search.rb
index 3ae67deabe058547eeae84f31c637796fcc2e94a..dea90b014e782a4b153d0ae47d7466de6d87b0b7 100644
--- a/features/steps/project/elastic/snippets_search.rb
+++ b/features/steps/project/elastic/snippets_search.rb
@@ -3,6 +3,7 @@ class Spinach::Features::SnippetsSearch < Spinach::FeatureSteps
   include SharedPaths
   include SharedProject
   include SharedElastic
+  include StubConfiguration
 
   before do
     Snippet.__elasticsearch__.create_index!
@@ -11,7 +12,7 @@ class Spinach::Features::SnippetsSearch < Spinach::FeatureSteps
   after do
     Snippet.__elasticsearch__.delete_index!
 
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(false)
+    stub_application_setting(elasticsearch_search: false, elasticsearch_indexing: false)
   end
 
   step 'there is a snippet "index" with "php rocks" string' do
diff --git a/features/steps/project/fork.rb b/features/steps/project/fork.rb
index 527f7853da9f093a2ea9f1ddce752ec403f7a534..8abeb5ee24202020c5f2c185f90b3a836c2c4b52 100644
--- a/features/steps/project/fork.rb
+++ b/features/steps/project/fork.rb
@@ -36,7 +36,7 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps
   end
 
   step 'I goto the Merge Requests page' do
-    page.within '.page-sidebar-expanded' do
+    page.within '.layout-nav' do
       click_link "Merge Requests"
     end
   end
diff --git a/features/steps/project/issues/award_emoji.rb b/features/steps/project/issues/award_emoji.rb
index c5d45709b445675d6e6dde3ce0662c9ced86a87b..1b14659b4dfb7ccdedba8a383e13c46c731bf6b3 100644
--- a/features/steps/project/issues/award_emoji.rb
+++ b/features/steps/project/issues/award_emoji.rb
@@ -39,8 +39,8 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps
 
   step 'I can see the activity and food categories' do
     page.within '.emoji-menu' do
-      expect(page).to_not have_selector 'Activity'
-      expect(page).to_not have_selector 'Food'
+      expect(page).not_to have_selector 'Activity'
+      expect(page).not_to have_selector 'Food'
     end
   end
 
diff --git a/features/steps/project/issues/filter_labels.rb b/features/steps/project/issues/filter_labels.rb
index d82c68569182e2d0d6e03da21d6f91d109abaaee..d34fa694789f68722d2176985a42268c62b6de75 100644
--- a/features/steps/project/issues/filter_labels.rb
+++ b/features/steps/project/issues/filter_labels.rb
@@ -29,7 +29,7 @@ class Spinach::Features::ProjectIssuesFilterLabels < Spinach::FeatureSteps
   end
 
   step 'I click link "bug"' do
-    page.find('.js-label-select').click
+    page.find('.js-label-select', visible: true).click
     sleep 0.5
     execute_script("$('.dropdown-menu-labels li:contains(\"bug\") a').click()")
   end
diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb
index fc12843ea5ca4a3a692388604107aaaed1366e8b..439363e6f141d6b15142bb04af886275f5a267e5 100644
--- a/features/steps/project/issues/issues.rb
+++ b/features/steps/project/issues/issues.rb
@@ -191,15 +191,15 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
   end
 
   step 'issue "Release 0.4" have 2 upvotes and 1 downvote' do
-    issue = Issue.find_by(title: 'Release 0.4')
-    create_list(:upvote_note, 2, project: project, noteable: issue)
-    create(:downvote_note, project: project, noteable: issue)
+    awardable = Issue.find_by(title: 'Release 0.4')
+    create_list(:award_emoji, 2, awardable: awardable)
+    create(:award_emoji, :downvote, awardable: awardable)
   end
 
   step 'issue "Tweet control" have 1 upvote and 2 downvotes' do
-    issue = Issue.find_by(title: 'Tweet control')
-    create(:upvote_note, project: project, noteable: issue)
-    create_list(:downvote_note, 2, project: project, noteable: issue)
+    awardable = Issue.find_by(title: 'Tweet control')
+    create(:award_emoji, :upvote, awardable: awardable)
+    create_list(:award_emoji, 2, awardable: awardable, name: 'thumbsdown')
   end
 
   step 'The list should be sorted by "Least popular"' do
@@ -216,7 +216,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
 
       page.within 'li.issue:nth-child(3)' do
         expect(page).to have_content 'Bugfix'
-        expect(page).to_not have_content '0 0'
+        expect(page).not_to have_content '0 0'
       end
     end
   end
@@ -235,7 +235,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
 
       page.within 'li.issue:nth-child(3)' do
         expect(page).to have_content 'Bugfix'
-        expect(page).to_not have_content '0 0'
+        expect(page).not_to have_content '0 0'
       end
     end
   end
@@ -348,7 +348,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
 
   step 'another user adds a comment with text "Yay!" to issue "Release 0.4"' do
     issue = Issue.find_by!(title: 'Release 0.4')
-    create(:note_on_issue, noteable: issue,  note: 'Yay!')
+    create(:note_on_issue, noteable: issue, project: project, note: 'Yay!')
   end
 
   step 'I should see a new comment with text "Yay!"' do
diff --git a/features/steps/project/issues/labels.rb b/features/steps/project/issues/labels.rb
index 0ca2d6257c3bc889c9b095f6437f070d0685b0a2..2937d5d7ca8a6647a7235feaff8c11d28fba6db2 100644
--- a/features/steps/project/issues/labels.rb
+++ b/features/steps/project/issues/labels.rb
@@ -9,7 +9,7 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps
 
   step 'I remove label \'bug\'' do
     page.within "#label_#{bug_label.id}" do
-      click_link 'Delete'
+      first(:link, 'Delete').click
     end
   end
 
@@ -24,8 +24,8 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps
 
   step 'I should see labels help message' do
     page.within '.labels' do
-      expect(page).to have_content 'Create first label or generate default set of '\
-                               'labels'
+      expect(page).to have_content 'Create a label or generate a default set '\
+                                   'of labels'
     end
   end
 
@@ -60,25 +60,25 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps
   end
 
   step 'I should see label \'feature\'' do
-    page.within '.manage-labels-list' do
+    page.within '.other-labels .manage-labels-list' do
       expect(page).to have_content 'feature'
     end
   end
 
   step 'I should see label \'bug\'' do
-    page.within '.manage-labels-list' do
+    page.within '.other-labels .manage-labels-list' do
       expect(page).to have_content 'bug'
     end
   end
 
   step 'I should not see label \'bug\'' do
-    page.within '.manage-labels-list' do
+    page.within '.other-labels .manage-labels-list' do
       expect(page).not_to have_content 'bug'
     end
   end
 
   step 'I should see label \'support\'' do
-    page.within '.manage-labels-list' do
+    page.within '.other-labels .manage-labels-list' do
       expect(page).to have_content 'support'
     end
   end
@@ -90,7 +90,7 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps
   end
 
   step 'I should see label \'fix\'' do
-    page.within '.manage-labels-list' do
+    page.within '.other-labels .manage-labels-list' do
       expect(page).to have_content 'fix'
     end
   end
diff --git a/features/steps/project/labels.rb b/features/steps/project/labels.rb
index 5bb021890214793501ef766d58516e0c2e370fc2..118ffef47740fea0d9156064549606dbc576101f 100644
--- a/features/steps/project/labels.rb
+++ b/features/steps/project/labels.rb
@@ -29,6 +29,6 @@ class Spinach::Features::Labels < Spinach::FeatureSteps
   private
 
   def subscribe_button
-    first('.label-subscribe-button span')
+    first('.js-subscribe-button', visible: true)
   end
 end
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index a772ce5e8559e27f5b6f22b6a49ebeff0b59abc7..e2df38e8a4b609e8b8d1bbc42271ba213cc2824d 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -179,14 +179,14 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
 
   step 'merge request "Bug NS-04" have 2 upvotes and 1 downvote' do
     merge_request = MergeRequest.find_by(title: 'Bug NS-04')
-    create_list(:upvote_note, 2, project: project, noteable: merge_request)
-    create(:downvote_note, project: project, noteable: merge_request)
+    create_list(:award_emoji, 2, awardable: merge_request)
+    create(:award_emoji, :downvote, awardable: merge_request)
   end
 
   step 'merge request "Bug NS-06" have 1 upvote and 2 downvotes' do
-    merge_request = MergeRequest.find_by(title: 'Bug NS-06')
-    create(:upvote_note, project: project, noteable: merge_request)
-    create_list(:downvote_note, 2, project: project, noteable: merge_request)
+    awardable = MergeRequest.find_by(title: 'Bug NS-06')
+    create(:award_emoji, awardable: awardable)
+    create_list(:award_emoji, 2, :downvote, awardable: awardable)
   end
 
   step 'The list should be sorted by "Least popular"' do
@@ -203,7 +203,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
 
       page.within 'li.merge-request:nth-child(3)' do
         expect(page).to have_content 'Bug NS-05'
-        expect(page).to_not have_content '0 0'
+        expect(page).not_to have_content '0 0'
       end
     end
   end
@@ -222,7 +222,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
 
       page.within 'li.merge-request:nth-child(3)' do
         expect(page).to have_content 'Bug NS-05'
-        expect(page).to_not have_content '0 0'
+        expect(page).not_to have_content '0 0'
       end
     end
   end
@@ -273,7 +273,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
   step 'user "John Doe" leaves a comment like "Line is wrong" on diff' do
     mr = MergeRequest.find_by(title: "Bug NS-05")
     create(:note_on_merge_request_diff, project: project,
-                                        noteable_id: mr.id,
+                                        noteable: mr,
                                         author: user_exists("John Doe"),
                                         line_code: sample_commit.line_code,
                                         note: 'Line is wrong')
@@ -654,8 +654,8 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
   step '"Bug NS-05" has CI status' do
     project = merge_request.source_project
     project.enable_ci
-    ci_commit = create :ci_commit, project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch
-    create :ci_build, commit: ci_commit
+    pipeline = create :ci_pipeline, project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch
+    create :ci_build, pipeline: pipeline
   end
 
   step 'I should see merge request "Bug NS-05" with CI status' do
@@ -702,7 +702,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
     click_diff_line(sample_compare.changes[1][:line_code])
   end
 
-  def have_visible_content (text)
+  def have_visible_content(text)
     have_css("*", text: text, visible: true)
   end
 
diff --git a/features/steps/project/pages.rb b/features/steps/project/pages.rb
index d4fd9b3d81fa55b3779d6212e8bde91f9abbbb70..a76672168fb3c93e318b34d3a5e225f790708eaf 100644
--- a/features/steps/project/pages.rb
+++ b/features/steps/project/pages.rb
@@ -27,13 +27,13 @@ class Spinach::Features::ProjectPages < Spinach::FeatureSteps
   end
 
   step 'pages are deployed' do
-    commit = @project.ensure_ci_commit(@project.commit('HEAD').sha, 'HEAD')
+    pipeline = @project.ensure_pipeline(@project.commit('HEAD').sha, 'HEAD')
     build = build(:ci_build,
-                   project: @project,
-                   commit: commit,
-                   ref: 'HEAD',
-                   artifacts_file: fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip'),
-                   artifacts_metadata: fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip.meta')
+                  project: @project,
+                  pipeline: pipeline,
+                  ref: 'HEAD',
+                  artifacts_file: fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip'),
+                  artifacts_metadata: fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip.meta')
                  )
     result = ::Projects::UpdatePagesService.new(@project, build).execute
     expect(result[:status]).to eq(:success)
diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb
index 819fdefacc1733c3ab5e9f3ec02914c29bff96ce..e78c9b2254dae312a1a2f11f6638ab2621f87296 100644
--- a/features/steps/project/project.rb
+++ b/features/steps/project/project.rb
@@ -139,7 +139,9 @@ class Spinach::Features::Project < Spinach::FeatureSteps
   end
 
   step 'I should not see "Snippets" button' do
-    expect(page).not_to have_link 'Snippets'
+    page.within '.content' do
+      expect(page).not_to have_link 'Snippets'
+    end
   end
 
   step 'project "Shop" belongs to group' do
@@ -148,16 +150,8 @@ class Spinach::Features::Project < Spinach::FeatureSteps
     @project.save!
   end
 
-  step 'I should see back to dashboard button' do
-    expect(page).to have_content 'Go to dashboard'
-  end
-
-  step 'I should see back to group button' do
-    expect(page).to have_content 'Go to group'
-  end
-
   step 'I click notifications drop down button' do
-    click_link 'notifications-button'
+    find('#notifications-button').click
   end
 
   step 'I choose Mention setting' do
diff --git a/features/steps/project/project_find_file.rb b/features/steps/project/project_find_file.rb
index 8c1d09d6cc6aaeffc906d640bd87dca848299207..47de4b91df1ebaa0b53f5f4af7e22e8c8bd6b8a6 100644
--- a/features/steps/project/project_find_file.rb
+++ b/features/steps/project/project_find_file.rb
@@ -13,12 +13,12 @@ class Spinach::Features::ProjectFindFile < Spinach::FeatureSteps
   end
 
   step 'I should see "find file" page' do
-    ensure_active_main_tab('Files')
+    ensure_active_main_tab('Code')
     expect(page).to have_selector('.file-finder-holder', count: 1)
   end
 
   step 'I fill in Find by path with "git"' do
-    ensure_active_main_tab('Files')
+    ensure_active_main_tab('Code')
     expect(page).to have_selector('.file-finder-holder', count: 1)
   end
 
diff --git a/features/steps/project/project_milestone.rb b/features/steps/project/project_milestone.rb
index 2508c09e36d8612c6ca6f4e0f680a5bca5c1847b..1864b3a2b52df776e0b96f994c3f8029fb376cb8 100644
--- a/features/steps/project/project_milestone.rb
+++ b/features/steps/project/project_milestone.rb
@@ -52,7 +52,7 @@ class Spinach::Features::ProjectMilestone < Spinach::FeatureSteps
   end
 
   step 'I click link "Labels"' do
-    page.within('.nav-links') do
+    page.within('.layout-nav .nav-links') do
       page.find(:xpath, "//a[@href='#tab-labels']").click
     end
   end
diff --git a/features/steps/project/project_shortcuts.rb b/features/steps/project/project_shortcuts.rb
index 49e9c5520bb6031d336aa4236020643d92b24e60..8143b01ca4070fcfcf9c563b671c4045c07ee2e2 100644
--- a/features/steps/project/project_shortcuts.rb
+++ b/features/steps/project/project_shortcuts.rb
@@ -3,6 +3,7 @@ class Spinach::Features::ProjectShortcuts < Spinach::FeatureSteps
   include SharedPaths
   include SharedProject
   include SharedProjectTab
+  include SharedShortcuts
 
   step 'I press "g" and "f"' do
     find('body').native.send_key('g')
diff --git a/features/steps/project/snippets.rb b/features/steps/project/snippets.rb
index 786a0cad97571599525bea9761ad02d21ad8d3ce..beb8ecfc799254150314886eda3110190a55ab59 100644
--- a/features/steps/project/snippets.rb
+++ b/features/steps/project/snippets.rb
@@ -43,12 +43,12 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps
 
   step 'I click link "Edit"' do
     page.within ".detail-page-header" do
-      click_link "Edit"
+      first(:link, "Edit").click
     end
   end
 
   step 'I click link "Delete"' do
-    click_link "Delete"
+    first(:link, "Delete").click
   end
 
   step 'I submit new snippet "Snippet three"' do
diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb
index c26d7a1521214cd1f52d8e408cd5261a19257a36..79a3ed8197e46ec9cb44a17d4344038247354d6e 100644
--- a/features/steps/project/source/browse_files.rb
+++ b/features/steps/project/source/browse_files.rb
@@ -202,8 +202,8 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
   end
 
   step 'I see Browse dir link' do
-    expect(page).to have_link 'Browse Directory »'
-    expect(page).not_to have_link 'Browse Code »'
+    expect(page).to have_link 'Browse Directory'
+    expect(page).not_to have_link 'Browse Code'
   end
 
   step 'I click on readme file' do
@@ -219,7 +219,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
 
   step 'I see Browse code link' do
     expect(page).to have_link 'Browse Files'
-    expect(page).not_to have_link 'Browse Directory »'
+    expect(page).not_to have_link 'Browse Directory'
   end
 
   step 'I click on Permalink' do
@@ -337,13 +337,15 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
   end
 
   step 'I should see buttons for allowed commands' do
-    expect(page).to have_content 'Raw'
-    expect(page).to have_content 'History'
-    expect(page).to have_content 'Permalink'
-    expect(page).not_to have_content 'Edit'
-    expect(page).not_to have_content 'Blame'
-    expect(page).to have_content 'Delete'
-    expect(page).to have_content 'Replace'
+    page.within '.content' do
+      expect(page).to have_content 'Raw'
+      expect(page).to have_content 'History'
+      expect(page).to have_content 'Permalink'
+      expect(page).not_to have_content 'Edit'
+      expect(page).not_to have_content 'Blame'
+      expect(page).to have_content 'Delete'
+      expect(page).to have_content 'Replace'
+    end
   end
 
   step 'I should see a notice about a new fork having been created' do
diff --git a/features/steps/project/team_management.rb b/features/steps/project/team_management.rb
index c6ced747370c3c75dd15e9ed5ceca9de755a5eaa..f32576d2cb1b6dcbd33acb87adc56eebf0c5ae44 100644
--- a/features/steps/project/team_management.rb
+++ b/features/steps/project/team_management.rb
@@ -26,8 +26,11 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
   end
 
   step 'I should see "Mike" in team list as "Reporter"' do
-    page.within ".access-reporter" do
+    user = User.find_by(name: 'Mike')
+    project_member = project.project_members.find_by(user_id: user.id)
+    page.within "#project_member_#{project_member.id}" do
       expect(page).to have_content('Mike')
+      expect(page).to have_content('Reporter')
     end
   end
 
@@ -40,16 +43,20 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
   end
 
   step 'I should see "sjobs@apple.com" in team list as invited "Reporter"' do
-    page.within ".access-reporter" do
+    project_member = project.project_members.find_by(invite_email: 'sjobs@apple.com')
+    page.within "#project_member_#{project_member.id}" do
       expect(page).to have_content('sjobs@apple.com')
-      expect(page).to have_content('invited')
+      expect(page).to have_content('Invited')
       expect(page).to have_content('Reporter')
     end
   end
 
   step 'I should see "Dmitriy" in team list as "Developer"' do
-    page.within ".access-developer" do
+    user = User.find_by(name: 'Dmitriy')
+    project_member = project.project_members.find_by(user_id: user.id)
+    page.within "#project_member_#{project_member.id}" do
       expect(page).to have_content('Dmitriy')
+      expect(page).to have_content('Developer')
     end
   end
 
@@ -65,15 +72,14 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
   end
 
   step 'I should see "Dmitriy" in team list as "Reporter"' do
-    page.within ".access-reporter" do
+    user = User.find_by(name: 'Dmitriy')
+    project_member = project.project_members.find_by(user_id: user.id)
+    page.within "#project_member_#{project_member.id}" do
       expect(page).to have_content('Dmitriy')
+      expect(page).to have_content('Reporter')
     end
   end
 
-  step 'I click link "Remove from team"' do
-    click_link "Remove from team"
-  end
-
   step 'I should not see "Dmitriy" in team list' do
     user = User.find_by(name: "Dmitriy")
     expect(page).not_to have_content(user.name)
@@ -120,7 +126,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
     user = User.find_by(name: 'Dmitriy')
     project_member = project.project_members.find_by(user_id: user.id)
     page.within "#project_member_#{project_member.id}" do
-      click_link('Remove user from team')
+      click_link('Remove user from project')
     end
   end
 
diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb
index c6191314aaa40b3edb49fa99a09c822766e688cb..4634ed1f25e41fd2559f9e1437b29e57705f9482 100644
--- a/features/steps/project/wiki.rb
+++ b/features/steps/project/wiki.rb
@@ -76,7 +76,8 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
   end
 
   step 'I click on the "Pages" button' do
-    click_on "Pages"
+    wiki_menu = find('.content .nav-links')
+    wiki_menu.click_on "Pages"
   end
 
   step 'I should see the existing page in the pages list' do
@@ -95,9 +96,9 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
 
   step 'I click on existing image link' do
     file = Gollum::File.new(wiki.wiki)
-    allow_any_instance_of(Gollum::Wiki).to receive(:file).with("image.jpg", "master", true).and_return(file)
-    allow_any_instance_of(Gollum::File).to receive(:mime_type).and_return("image/jpeg")
-    expect(page).to have_link('image', href: "image.jpg")
+    Gollum::Wiki.any_instance.stub(:file).with("image.jpg", "master", true).and_return(file)
+    Gollum::File.any_instance.stub(:mime_type).and_return("image/jpeg")
+    expect(page).to have_link('image', href: "#{wiki.wiki_base_path}/image.jpg")
     click_on "image"
   end
 
@@ -113,7 +114,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
   end
 
   step 'I click on image link' do
-    expect(page).to have_link('image', href: "image.jpg")
+    expect(page).to have_link('image', href: "#{wiki.wiki_base_path}/image.jpg")
     click_on "image"
   end
 
diff --git a/features/steps/shared/active_tab.rb b/features/steps/shared/active_tab.rb
index 0bee91d758d6859ead46636e5ee4339ac7ef23fa..4eef7aff213f126543019f7f17bac53c7ea4b3a4 100644
--- a/features/steps/shared/active_tab.rb
+++ b/features/steps/shared/active_tab.rb
@@ -2,46 +2,26 @@ module SharedActiveTab
   include Spinach::DSL
 
   def ensure_active_main_tab(content)
-    expect(find('.nav-sidebar > li.active')).to have_content(content)
+    expect(find('.layout-nav li.active')).to have_content(content)
   end
 
   def ensure_active_sub_tab(content)
-    expect(find('div.content ul.nav-links li.active')).to have_content(content)
+    expect(find('.sub-nav li.active')).to have_content(content)
   end
 
   def ensure_active_sub_nav(content)
-    expect(find('.sidebar-subnav > li.active')).to have_content(content)
+    expect(find('.layout-nav .controls li.active')).to have_content(content)
   end
 
   step 'no other main tabs should be active' do
-    expect(page).to have_selector('.nav-sidebar > li.active', count: 1)
+    expect(page).to have_selector('.layout-nav .nav-links > li.active', count: 1)
   end
 
   step 'no other sub tabs should be active' do
-    expect(page).to have_selector('div.content ul.nav-links li.active', count: 1)
+    expect(page).to have_selector('.sub-nav li.active', count: 1)
   end
 
   step 'no other sub navs should be active' do
-    expect(page).to have_selector('.sidebar-subnav > li.active', count: 1)
-  end
-
-  step 'the active main tab should be Home' do
-    ensure_active_main_tab('Projects')
-  end
-
-  step 'the active main tab should be Projects' do
-    ensure_active_main_tab('Projects')
-  end
-
-  step 'the active main tab should be Issues' do
-    ensure_active_main_tab('Issues')
-  end
-
-  step 'the active main tab should be Merge Requests' do
-    ensure_active_main_tab('Merge Requests')
-  end
-
-  step 'the active main tab should be Help' do
-    ensure_active_main_tab('Help')
+    expect(page).to have_selector('.layout-nav .controls li.active', count: 1)
   end
 end
diff --git a/features/steps/shared/builds.rb b/features/steps/shared/builds.rb
index cf30e23b6bd35f4e1b86c8de578295b52ab3957c..4d6b258f5778d11876204e144ceb50656cc8d993 100644
--- a/features/steps/shared/builds.rb
+++ b/features/steps/shared/builds.rb
@@ -10,8 +10,8 @@ module SharedBuilds
   end
 
   step 'project has a recent build' do
-    @ci_commit = create(:ci_commit, project: @project, sha: @project.commit.sha, ref: 'master')
-    @build = create(:ci_build_with_coverage, commit: @ci_commit)
+    @pipeline = create(:ci_pipeline, project: @project, sha: @project.commit.sha, ref: 'master')
+    @build = create(:ci_build_with_coverage, pipeline: @pipeline)
   end
 
   step 'recent build is successful' do
@@ -23,7 +23,7 @@ module SharedBuilds
   end
 
   step 'project has another build that is running' do
-    create(:ci_build, commit: @ci_commit, name: 'second build', status: 'running')
+    create(:ci_build, pipeline: @pipeline, name: 'second build', status: 'running')
   end
 
   step 'I visit recent build details page' do
diff --git a/features/steps/shared/elastic.rb b/features/steps/shared/elastic.rb
index 20b01458fdd36a2c26158bdc787d4c4d2b517dbd..0cd8c41f421a357c1219e9eb0cf42ed0d57fcd5e 100644
--- a/features/steps/shared/elastic.rb
+++ b/features/steps/shared/elastic.rb
@@ -25,7 +25,7 @@ module SharedElastic
   end
 
   step 'Elasticsearch is enabled' do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(true)
+    stub_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
   end
 
   def select_filter(name)
diff --git a/features/steps/shared/issuable.rb b/features/steps/shared/issuable.rb
index a58b3cb7e160426195aa7e06a21da6e6ddb3e193..c6572cf386ef50affab220c37c30feafcb302640 100644
--- a/features/steps/shared/issuable.rb
+++ b/features/steps/shared/issuable.rb
@@ -111,7 +111,7 @@ module SharedIssuable
 
   step 'I sort the list by "Oldest updated"' do
     find('button.dropdown-toggle.btn').click
-    page.within('ul.dropdown-menu.dropdown-menu-align-right li') do
+    page.within('.content ul.dropdown-menu.dropdown-menu-align-right li') do
       click_link "Oldest updated"
     end
   end
@@ -119,7 +119,7 @@ module SharedIssuable
   step 'I sort the list by "Least popular"' do
     find('button.dropdown-toggle.btn').click
 
-    page.within('ul.dropdown-menu.dropdown-menu-align-right li') do
+    page.within('.content ul.dropdown-menu.dropdown-menu-align-right li') do
       click_link 'Least popular'
     end
   end
@@ -127,33 +127,17 @@ module SharedIssuable
   step 'I sort the list by "Most popular"' do
     find('button.dropdown-toggle.btn').click
 
-    page.within('ul.dropdown-menu.dropdown-menu-align-right li') do
+    page.within('.content ul.dropdown-menu.dropdown-menu-align-right li') do
       click_link 'Most popular'
     end
   end
 
   step 'The list should be sorted by "Oldest updated"' do
-    page.within('div.dropdown.inline.prepend-left-10') do
+    page.within('.content div.dropdown.inline.prepend-left-10') do
       expect(page.find('button.dropdown-toggle.btn')).to have_content('Oldest updated')
     end
   end
 
-  step 'I should see "1 of 1" in the sidebar' do
-    expect_sidebar_content('1 of 1')
-  end
-
-  step 'I should see "1 of 2" in the sidebar' do
-    expect_sidebar_content('1 of 2')
-  end
-
-  step 'I should see "2 of 2" in the sidebar' do
-    expect_sidebar_content('2 of 2')
-  end
-
-  step 'I should see "3 of 3" in the sidebar' do
-    expect_sidebar_content('3 of 3')
-  end
-
   step 'I click link "Next" in the sidebar' do
     page.within '.issuable-sidebar' do
       click_link 'Next'
diff --git a/features/steps/shared/note.rb b/features/steps/shared/note.rb
index a3c3887ab4600ec0acd1f25a8f815a318f43f479..3d7c6ef9d2db0636078f63e2c04696eac4027585 100644
--- a/features/steps/shared/note.rb
+++ b/features/steps/shared/note.rb
@@ -107,7 +107,7 @@ module SharedNote
   end
 
   step 'I should see no notes at all' do
-    expect(page).to_not have_css('.note')
+    expect(page).not_to have_css('.note')
   end
 
   # Markdown
diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb
index 6f5cbdec8964d10566fa8fd16b83b4aa9a8766e4..0e5cd98c15e8a4dffc7a15926573ef8743b92cd8 100644
--- a/features/steps/shared/project.rb
+++ b/features/steps/shared/project.rb
@@ -101,7 +101,7 @@ module SharedProject
   step 'I should see project settings' do
     expect(current_path).to eq edit_namespace_project_path(@project.namespace, @project)
     expect(page).to have_content("Project name")
-    expect(page).to have_content("Features:")
+    expect(page).to have_content("Features")
   end
 
   def current_project
@@ -236,7 +236,7 @@ module SharedProject
 
   step 'project "Shop" has CI build' do
     project = Project.find_by(name: "Shop")
-    create :ci_commit, project: project, sha: project.commit.sha, ref: 'master', status: 'skipped'
+    create :ci_pipeline, project: project, sha: project.commit.sha, ref: 'master', status: 'skipped'
   end
 
   step 'I should see last commit with CI status' do
diff --git a/features/steps/shared/project_tab.rb b/features/steps/shared/project_tab.rb
index 4fc2ece79ff927ba2e526490126c8b4e6d8f251f..bfee87933010d210f40d2b8f186f18da2c78ab3c 100644
--- a/features/steps/shared/project_tab.rb
+++ b/features/steps/shared/project_tab.rb
@@ -8,12 +8,8 @@ module SharedProjectTab
     ensure_active_main_tab('Project')
   end
 
-  step 'the active main tab should be Files' do
-    ensure_active_main_tab('Files')
-  end
-
-  step 'the active main tab should be Commits' do
-    ensure_active_main_tab('Commits')
+  step 'the active main tab should be Code' do
+    ensure_active_main_tab('Code')
   end
 
   step 'the active main tab should be Graphs' do
@@ -41,9 +37,7 @@ module SharedProjectTab
   end
 
   step 'the active main tab should be Settings' do
-    page.within '.nav-sidebar' do
-      expect(page).to have_content('Go to project')
-    end
+    expect(page).to have_selector('.layout-nav .nav-links > li.active', count: 0)
   end
 
   step 'the active main tab should be Activity' do
@@ -53,4 +47,12 @@ module SharedProjectTab
   step 'the active sub tab should be Network' do
     ensure_active_sub_tab('Network')
   end
+
+  step 'the active sub tab should be Files' do
+    ensure_active_sub_tab('Files')
+  end
+
+  step 'the active sub tab should be Commits' do
+    ensure_active_sub_tab('Commits')
+  end
 end
diff --git a/features/steps/shared/shortcuts.rb b/features/steps/shared/shortcuts.rb
index bbb7afec0ad33e98deed901f55175438ec31ab57..a75a8474d26a7f7b26c4c944399ca08aa29579e8 100644
--- a/features/steps/shared/shortcuts.rb
+++ b/features/steps/shared/shortcuts.rb
@@ -1,4 +1,4 @@
-module SharedActiveTab
+module SharedShortcuts
   include Spinach::DSL
 
   step 'I press "g" and "p"' do
diff --git a/features/steps/shared/sidebar_active_tab.rb b/features/steps/shared/sidebar_active_tab.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5c47238777fd77113aa51d2d7b150185afe19ed0
--- /dev/null
+++ b/features/steps/shared/sidebar_active_tab.rb
@@ -0,0 +1,35 @@
+module SharedSidebarActiveTab
+  include Spinach::DSL
+
+  step 'the active main tab should be Help' do
+    ensure_active_main_tab('Help')
+  end
+
+  step 'no other main tabs should be active' do
+    expect(page).to have_selector('.nav-sidebar > li.active', count: 1)
+  end
+
+  def ensure_active_main_tab(content)
+    expect(find('.nav-sidebar li.active')).to have_content(content)
+  end
+
+  step 'the active main tab should be Home' do
+    ensure_active_main_tab('Projects')
+  end
+
+  step 'the active main tab should be Projects' do
+    ensure_active_main_tab('Projects')
+  end
+
+  step 'the active main tab should be Issues' do
+    ensure_active_main_tab('Issues')
+  end
+
+  step 'the active main tab should be Merge Requests' do
+    ensure_active_main_tab('Merge Requests')
+  end
+
+  step 'the active main tab should be Help' do
+    ensure_active_main_tab('Help')
+  end
+end
diff --git a/features/steps/snippets/snippets.rb b/features/steps/snippets/snippets.rb
index 023032e679f219001de31470cb25f97410b823a9..19366b11071d71dae680395a5a8367d61ac68f3f 100644
--- a/features/steps/snippets/snippets.rb
+++ b/features/steps/snippets/snippets.rb
@@ -14,12 +14,12 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps
 
   step 'I click link "Edit"' do
     page.within ".detail-page-header" do
-      click_link "Edit"
+      first(:link, "Edit").click
     end
   end
 
   step 'I click link "Delete"' do
-    click_link "Delete"
+    first(:link, "Delete").click
   end
 
   step 'I submit new snippet "Personal snippet three"' do
diff --git a/features/steps/user.rb b/features/steps/user.rb
index 20c48f3f53c642aca391fe5550230b8ab5a6511c..77b92178b5f6aa51dc968fb43a4bef180e4aeb03 100644
--- a/features/steps/user.rb
+++ b/features/steps/user.rb
@@ -51,7 +51,7 @@ class Spinach::Features::User < Spinach::FeatureSteps
   end
 
   step 'I should see contributions calendar' do
-    expect(page).to have_css('.cal-heatmap-container')
+    expect(page).to have_css('.js-contrib-calendar')
   end
 
   def contributed_project
diff --git a/features/support/env.rb b/features/support/env.rb
index 5c26ce8cf4fdffba5f9ed04b57d792d757c8a4de..c1282993a32aceff69e330b7bd84ce4a963962eb 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -16,6 +16,11 @@ require_relative 'capybara'
 require_relative 'db_cleaner'
 require_relative 'rerun'
 
+if ENV['CI']
+  require 'knapsack'
+  Knapsack::Adapters::RSpecAdapter.bind
+end
+
 %w(select2_helper test_env repo_helpers license).each do |f|
   require Rails.root.join('spec', 'support', f)
 end
diff --git a/lib/api/api.rb b/lib/api/api.rb
index d9e6bd67e13a859cf72063b8bb433ae1bf1b3023..27898a46ded6ccc7e974ba5379de7ef59732be07 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -63,5 +63,6 @@ module API
     mount ::API::Runners
     mount ::API::LicenseTemplates
     mount ::API::Subscriptions
+    mount ::API::Gitignores
   end
 end
diff --git a/lib/api/builds.rb b/lib/api/builds.rb
index 2b104f90aa78402ade8c9971d396c23be2f45268..979328efe0edb2f9e2bdd37009263ad2db08c3ee 100644
--- a/lib/api/builds.rb
+++ b/lib/api/builds.rb
@@ -33,7 +33,7 @@ module API
       get ':id/repository/commits/:sha/builds' do
         authorize_read_builds!
 
-        commit = user_project.ci_commits.find_by_sha(params[:sha])
+        commit = user_project.pipelines.find_by_sha(params[:sha])
         return not_found! unless commit
 
         builds = commit.builds.order('id DESC')
@@ -142,7 +142,7 @@ module API
         return not_found!(build) unless build
         return forbidden!('Build is not retryable') unless build.retryable?
 
-        build = Ci::Build.retry(build)
+        build = Ci::Build.retry(build, current_user)
 
         present build, with: Entities::Build,
                        user_can_download_artifacts: can?(current_user, :read_build, user_project)
@@ -166,6 +166,26 @@ module API
         present build, with: Entities::Build,
                        user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project)
       end
+
+      # Keep the artifacts to prevent them from being deleted
+      #
+      # Parameters:
+      #   id (required) - the id of a project
+      #   build_id (required) - The ID of a build
+      # Example Request:
+      #   POST /projects/:id/builds/:build_id/artifacts/keep
+      post ':id/builds/:build_id/artifacts/keep' do
+        authorize_update_builds!
+
+        build = get_build(params[:build_id])
+        return not_found!(build) unless build && build.artifacts?
+
+        build.keep_artifacts!
+
+        status 200
+        present build, with: Entities::Build,
+                       user_can_download_artifacts: can?(current_user, :read_build, user_project)
+      end
     end
 
     helpers do
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index 9bcd33ff19ebb11fc9f0a98267b6d51e7019b302..323a70868900afd1277eada973162f2a70cb8375 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -22,8 +22,8 @@ module API
 
         not_found!('Commit') unless user_project.commit(params[:sha])
 
-        ci_commits = user_project.ci_commits.where(sha: params[:sha])
-        statuses = ::CommitStatus.where(commit: ci_commits)
+        pipelines = user_project.pipelines.where(sha: params[:sha])
+        statuses = ::CommitStatus.where(pipeline: pipelines)
         statuses = statuses.latest unless parse_boolean(params[:all])
         statuses = statuses.where(ref: params[:ref]) if params[:ref].present?
         statuses = statuses.where(stage: params[:stage]) if params[:stage].present?
@@ -50,7 +50,7 @@ module API
         commit = @project.commit(params[:sha])
         not_found! 'Commit' unless commit
 
-        # Since the CommitStatus is attached to Ci::Commit (in the future Pipeline)
+        # Since the CommitStatus is attached to Ci::Pipeline (in the future Pipeline)
         # We need to always have the pipeline object
         # To have a valid pipeline object that can be attached to specific MR
         # Other CI service needs to send `ref`
@@ -64,11 +64,11 @@ module API
           ref = branches.first
         end
 
-        ci_commit = @project.ensure_ci_commit(commit.sha, ref)
+        pipeline = @project.ensure_pipeline(commit.sha, ref)
 
         name = params[:name] || params[:context]
-        status = GenericCommitStatus.running_or_pending.find_by(commit: ci_commit, name: name, ref: params[:ref])
-        status ||= GenericCommitStatus.new(project: @project, commit: ci_commit, user: current_user)
+        status = GenericCommitStatus.running_or_pending.find_by(pipeline: pipeline, name: name, ref: params[:ref])
+        status ||= GenericCommitStatus.new(project: @project, pipeline: pipeline, user: current_user)
         status.update(attrs)
 
         case params[:state].to_s
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 7fe7146f236c5dbd28da408945f203580dba2155..d93538e503c90a9b4813d356aacf9c4769d875b3 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -30,7 +30,7 @@ module API
       expose :identities, using: Entities::Identity
       expose :can_create_group?, as: :can_create_group
       expose :can_create_project?, as: :can_create_project
-      expose :two_factor_enabled
+      expose :two_factor_enabled?, as: :two_factor_enabled
       expose :external
     end
 
@@ -103,9 +103,7 @@ module API
         if: lambda { |group, options| group.ldap_group_links.any? }
 
       expose :avatar_url
-      expose :web_url do |group, options|
-        Gitlab::Routing.url_helpers.group_url(group)
-      end
+      expose :web_url
     end
 
     class GroupDetail < Group
@@ -185,15 +183,22 @@ module API
       expose :label_names, as: :labels
       expose :milestone, using: Entities::Milestone
       expose :assignee, :author, using: Entities::UserBasic
+
       expose :subscribed do |issue, options|
         issue.subscribed?(options[:current_user])
       end
       expose :user_notes_count
+      expose :upvotes, :downvotes
+    end
+
+    class ExternalIssue < Grape::Entity
+      expose :title
+      expose :id
     end
 
     class MergeRequest < ProjectEntity
       expose :target_branch, :source_branch
-      expose :upvotes,  :downvotes
+      expose :upvotes, :downvotes
       expose :author, :assignee, using: Entities::UserBasic
       expose :source_project_id, :target_project_id
       expose :label_names, as: :labels
@@ -214,6 +219,17 @@ module API
       end
     end
 
+    class Approvals < Grape::Entity
+      expose :user, using: Entities::UserBasic
+    end
+
+    class MergeRequestApprovals < ProjectEntity
+      expose :merge_status
+      expose :approvals_required
+      expose :approvals_left
+      expose :approvals, as: :approved_by, using: Entities::Approvals
+    end
+
     class SSHKey < Grape::Entity
       expose :id, :title, :key, :created_at
     end
@@ -231,8 +247,8 @@ module API
       expose :system?, as: :system
       expose :noteable_id, :noteable_type
       # upvote? and downvote? are deprecated, always return false
-      expose :upvote?, as: :upvote
-      expose :downvote?, as: :downvote
+      expose(:upvote?)    { |note| false }
+      expose(:downvote?)  { |note| false }
     end
 
     class MRNote < Grape::Entity
@@ -367,6 +383,7 @@ module API
       expose :signin_enabled
       expose :gravatar_enabled
       expose :sign_in_text
+      expose :after_sign_up_text
       expose :created_at
       expose :updated_at
       expose :home_page_url
@@ -380,6 +397,7 @@ module API
       expose :restricted_signup_domains
       expose :user_oauth_applications
       expose :after_sign_out_path
+      expose :container_registry_token_expire_delay
     end
 
     class Release < Grape::Entity
@@ -487,5 +505,13 @@ module API
       expose(:limitations) { |license| license.meta['limitations'] }
       expose :content
     end
+
+    class GitignoresList < Grape::Entity
+      expose :name
+    end
+
+    class Gitignore < Grape::Entity
+      expose :name, :content
+    end
   end
 end
diff --git a/lib/api/gitignores.rb b/lib/api/gitignores.rb
new file mode 100644
index 0000000000000000000000000000000000000000..270c9501dd2271b3f58e834b56145feeea9dab12
--- /dev/null
+++ b/lib/api/gitignores.rb
@@ -0,0 +1,29 @@
+module API
+  class Gitignores < Grape::API
+
+    # Get the list of the available gitignore templates
+    #
+    # Example Request:
+    #   GET /gitignores
+    get 'gitignores' do
+      present Gitlab::Gitignore.all, with: Entities::GitignoresList
+    end
+
+    # Get the text for a specific gitignore
+    #
+    # Parameters:
+    #   name (required) - The name of a license
+    #
+    # Example Request:
+    #   GET /gitignores/Elixir
+    #
+    get 'gitignores/:name' do
+      required_attributes! [:name]
+
+      gitignore = Gitlab::Gitignore.find(params[:name])
+      not_found!('.gitignore') unless gitignore
+
+      present gitignore, with: Entities::Gitignore
+    end
+  end
+end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index e2af4177c56bbe37deddc6fa345dd2ea32a2a9cc..5ff7c040d964ef1e8e9a41fc64ed243065cbe12a 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -109,8 +109,7 @@ module API
       #   GET /groups/:id/projects
       get ":id/projects" do
         group = find_group(params[:id])
-        projects = group.projects
-        projects = filter_projects(projects)
+        projects = GroupProjectsFinder.new(group).execute(current_user)
         projects = paginate projects
         present projects, with: Entities::Project
       end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 37f63efa15be61266c467b6da45371b75058423e..61dbdf79a8c0295437f182565d49ef6f6c1b7c80 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -29,7 +29,7 @@ module API
       @current_user
     end
 
-    def sudo_identifier()
+    def sudo_identifier
       identifier ||= params[SUDO_PARAM] || env[SUDO_HEADER]
 
       # Regex for integers
@@ -419,5 +419,23 @@ module API
       error!(errors[:access_level], 422) if errors[:access_level].any?
       not_found!(errors)
     end
+
+    def send_git_blob(repository, blob)
+      env['api.format'] = :txt
+      content_type 'text/plain'
+      header(*Gitlab::Workhorse.send_git_blob(repository, blob))
+    end
+
+    def send_git_archive(repository, ref:, format:)
+      header(*Gitlab::Workhorse.send_git_archive(repository, ref: ref, format: format))
+    end
+
+    def issue_entity(project)
+      if project.has_external_issue_tracker?
+        Entities::ExternalIssue
+      else
+        Entities::Issue
+      end
+    end
   end
 end
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index 68c091d899e3cb757bfc75cfb8b8d5d09e8fe918..c283fa92ba22f8db73b146756b7e7a5918b40e26 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -23,8 +23,6 @@ module API
       end
 
       post "/allowed" do
-        Gitlab::Metrics.action = 'Grape#/internal/allowed'
-
         status 200
 
         actor =
@@ -58,8 +56,6 @@ module API
       # Get a ssh key using the fingerprint
       #
       get "/authorized_keys" do
-        Gitlab::Metrics.action = 'Grape#/internal/authorized_keys'
-
         fingerprint = params.fetch(:fingerprint) do
           Gitlab::InsecureKeyFingerprint.new(params.fetch(:key)).fingerprint
         end
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index f59a4d6c012df1887ecc4d05d076c8b9ce5aadf4..4c43257c48a75dffb7ea70f999d59e91669a23b5 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -51,7 +51,7 @@ module API
       #   GET /issues?labels=foo,bar
       #   GET /issues?labels=foo,bar&state=opened
       get do
-        issues = current_user.issues
+        issues = current_user.issues.inc_notes_with_associations
         issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
         issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
         issues.reorder(issuable_order_by => issuable_sort)
@@ -82,7 +82,7 @@ module API
       #   GET /projects/:id/issues?milestone=1.0.0&state=closed
       #   GET /issues?iid=42
       get ":id/issues" do
-        issues = user_project.issues.visible_to_user(current_user)
+        issues = user_project.issues.inc_notes_with_associations.visible_to_user(current_user)
         issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
         issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
         issues = filter_by_iid(issues, params[:iid]) unless params[:iid].nil?
diff --git a/lib/api/license_templates.rb b/lib/api/license_templates.rb
index 62fa9fb6caf0ce43411e357c2a21f189edf7107c..b65db7510fb799138be7f9234835489f97434065 100644
--- a/lib/api/license_templates.rb
+++ b/lib/api/license_templates.rb
@@ -2,15 +2,15 @@ module API
   # Licenses API
   class LicenseTemplates < Grape::API
     PROJECT_TEMPLATE_REGEX =
-    /[\<\{\[]
-      (project|description|
-      one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here
-    [\>\}\]]/xi.freeze
+      /[\<\{\[]
+        (project|description|
+        one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here
+      [\>\}\]]/xi.freeze
     YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i.freeze
     FULLNAME_TEMPLATE_REGEX =
-    /[\<\{\[]
-      (fullname|name\sof\s(author|copyright\sowner))
-    [\>\}\]]/xi.freeze
+      /[\<\{\[]
+        (fullname|name\sof\s(author|copyright\sowner))
+      [\>\}\]]/xi.freeze
 
     # Get the list of the available license templates
     #
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 4e7de8867b43aeedd564d088876d1c9cbb037b55..8c41164db1f518689983891f941e3637bd68fd44 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -41,7 +41,7 @@ module API
       #
       get ":id/merge_requests" do
         authorize! :read_merge_request, user_project
-        merge_requests = user_project.merge_requests
+        merge_requests = user_project.merge_requests.inc_notes_with_associations
 
         unless params[:iid].nil?
           merge_requests = filter_by_iid(merge_requests, params[:iid])
@@ -218,6 +218,7 @@ module API
         #   merge_commit_message (optional)         - Custom merge commit message
         #   should_remove_source_branch (optional)  - When true, the source branch will be deleted if possible
         #   merge_when_build_succeeds (optional)    - When true, this MR will be merged when the build succeeds
+        #   sha (optional)                          - When present, must have the HEAD SHA of the source branch
         # Example:
         #   PUT /projects/:id/merge_requests/:merge_request_id/merge
         #
@@ -227,18 +228,21 @@ module API
           # Merge request can not be merged
           # because user dont have permissions to push into target branch
           unauthorized! unless merge_request.can_be_merged_by?(current_user)
-          not_allowed! if !merge_request.open? || merge_request.work_in_progress?
 
-          merge_request.check_if_can_be_merged
+          not_allowed! unless merge_request.mergeable_state?
 
-          render_api_error!('Branch cannot be merged', 406) unless merge_request.can_be_merged?
+          render_api_error!('Branch cannot be merged', 406) unless merge_request.mergeable?
+
+          if params[:sha] && merge_request.source_sha != params[:sha]
+            render_api_error!("SHA does not match HEAD of source branch: #{merge_request.source_sha}", 409)
+          end
 
           merge_params = {
             commit_message: params[:merge_commit_message],
             should_remove_source_branch: params[:should_remove_source_branch]
           }
 
-          if parse_boolean(params[:merge_when_build_succeeds]) && merge_request.ci_commit && merge_request.ci_commit.active?
+          if parse_boolean(params[:merge_when_build_succeeds]) && merge_request.pipeline && merge_request.pipeline.active?
             ::MergeRequests::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user, merge_params).
               execute(merge_request)
           else
@@ -325,7 +329,42 @@ module API
         get "#{path}/closes_issues" do
           merge_request = user_project.merge_requests.find(params[:merge_request_id])
           issues = ::Kaminari.paginate_array(merge_request.closes_issues(current_user))
-          present paginate(issues), with: Entities::Issue, current_user: current_user
+          present paginate(issues), with: issue_entity(user_project), current_user: current_user
+        end
+
+        # Get the status of the merge request's approvals
+        #
+        # Parameters:
+        #   id (required)                 - The ID of a project
+        #   merge_request_id (required)   - ID of MR
+        # Examples:
+        #   GET /projects/:id/merge_requests/:merge_request_id/approvals
+        #
+        get "#{path}/approvals" do
+          merge_request = user_project.merge_requests.find(params[:merge_request_id])
+
+          authorize! :read_merge_request, merge_request
+          present merge_request, with: Entities::MergeRequestApprovals, current_user: current_user
+        end
+
+        # Approve a merge request
+        #
+        # Parameters:
+        #   id (required)                 - The ID of a project
+        #   merge_request_id (required)   - ID of MR
+        # Examples:
+        #   POST /projects/:id/merge_requests/:merge_request_id/approvals
+        #
+        post "#{path}/approve" do
+          merge_request = user_project.merge_requests.find(params[:merge_request_id])
+
+          unauthorized! unless merge_request.can_approve?(current_user)
+
+          ::MergeRequests::ApprovalService
+            .new(user_project, current_user)
+            .execute(merge_request)
+
+          present merge_request, with: Entities::MergeRequestApprovals, current_user: current_user
         end
       end
     end
diff --git a/lib/api/project_members.rb b/lib/api/project_members.rb
index 2d880cf304254207f765674a883bded59d79aadc..7d247c1a9fe0587dd8776052bfa30bf00df9fdac 100644
--- a/lib/api/project_members.rb
+++ b/lib/api/project_members.rb
@@ -50,7 +50,7 @@ module API
         end
 
         # either the user is already a team member or a new one
-        project_member = user_project.project_member_by_id(params[:user_id])
+        project_member = user_project.project_member(params[:user_id])
         if project_member.nil?
           project_member = user_project.project_members.new(
             user_id: params[:user_id],
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 62161aadb9a09c714e95414978de82c05bd195eb..f55aceed92c357cb9f97ccd9f4090a6cc574612d 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -56,8 +56,7 @@ module API
         blob = Gitlab::Git::Blob.find(repo, commit.id, params[:filepath])
         not_found! "File" unless blob
 
-        content_type 'text/plain'
-        header *Gitlab::Workhorse.send_git_blob(repo, blob)
+        send_git_blob repo, blob
       end
 
       # Get a raw blob contents by blob sha
@@ -80,10 +79,7 @@ module API
 
         not_found! 'Blob' unless blob
 
-        env['api.format'] = :txt
-
-        content_type blob.mime_type
-        header *Gitlab::Workhorse.send_git_blob(repo, blob)
+        send_git_blob repo, blob
       end
 
       # Get a an archive of the repository
@@ -98,7 +94,7 @@ module API
         authorize! :download_code, user_project
 
         begin
-          header *Gitlab::Workhorse.send_git_archive(user_project, params[:sha], params[:format])
+          send_git_archive user_project.repository, ref: params[:sha], format: params[:format]
         rescue
           not_found!('File')
         end
diff --git a/lib/api/session.rb b/lib/api/session.rb
index cc6468959142170b5ac3a06145cf27aa5551149e..56c202f129435eedad105f015b8a845afb4f5994 100644
--- a/lib/api/session.rb
+++ b/lib/api/session.rb
@@ -11,8 +11,7 @@ module API
     # Example Request:
     #  POST /session
     post "/session" do
-      auth = Gitlab::Auth.new
-      user = auth.find(params[:email] || params[:login], params[:password])
+      user = Gitlab::Auth.find_with_user_password(params[:email] || params[:login], params[:password])
 
       return unauthorized! unless user
       present user, with: Entities::UserLogin
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 7def5f7e495f6d6f8e75e7e1433a79ab2d872536..99603b403c71d6f839787e035bc9243d99fdbbb4 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -78,7 +78,7 @@ module API
         required_attributes! [:email, :password, :name, :username]
         attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :bio, :location, :can_create_group, :admin, :confirm, :external]
         admin = attrs.delete(:admin)
-        confirm = !(attrs.delete(:confirm) =~ (/(false|f|no|0)$/i))
+        confirm = !(attrs.delete(:confirm) =~ /(false|f|no|0)$/i)
         user = User.build_user(attrs)
         user.admin = admin unless admin.nil?
         user.skip_confirmation! unless confirm
diff --git a/lib/award_emoji.rb b/lib/award_emoji.rb
deleted file mode 100644
index b1aecc2e671badbe25817acd05f98799e57afbf8..0000000000000000000000000000000000000000
--- a/lib/award_emoji.rb
+++ /dev/null
@@ -1,84 +0,0 @@
-class AwardEmoji
-  CATEGORIES = {
-    other: "Other",
-    objects: "Objects",
-    places: "Places",
-    travel_places: "Travel",
-    emoticons: "Emoticons",
-    objects_symbols: "Symbols",
-    nature: "Nature",
-    celebration: "Celebration",
-    people: "People",
-    activity: "Activity",
-    flags: "Flags",
-    food_drink: "Food"
-  }.with_indifferent_access
-
-  CATEGORY_ALIASES = {
-    symbols: "objects_symbols",
-    foods: "food_drink",
-    travel: "travel_places"
-  }.with_indifferent_access
-
-  def self.normilize_emoji_name(name)
-    aliases[name] || name
-  end
-
-  def self.emoji_by_category
-    unless @emoji_by_category
-      @emoji_by_category = Hash.new { |h, key| h[key] = [] }
-
-      emojis.each do |emoji_name, data|
-        data["name"] = emoji_name
-
-        # Skip Fitzpatrick(tone) modifiers
-        next if data["category"] == "modifier"
-
-        category = CATEGORY_ALIASES[data["category"]] || data["category"]
-
-        @emoji_by_category[category] << data
-      end
-
-      @emoji_by_category = @emoji_by_category.sort.to_h
-    end
-
-    @emoji_by_category
-  end
-
-  def self.emojis
-    @emojis ||= begin
-      json_path = File.join(Rails.root, 'fixtures', 'emojis', 'index.json' )
-      JSON.parse(File.read(json_path))
-    end
-  end
-
-  def self.unicode
-    @unicode ||= emojis.map {|key, value| { key => emojis[key]["unicode"] } }.inject(:merge!)
-  end
-
-  def self.aliases
-    @aliases ||= begin
-      json_path = File.join(Rails.root, 'fixtures', 'emojis', 'aliases.json' )
-      JSON.parse(File.read(json_path))
-    end
-  end
-
-  # Returns an Array of Emoji names and their asset URLs.
-  def self.urls
-    @urls ||= begin
-      path = File.join(Rails.root, 'fixtures', 'emojis', 'digests.json')
-      prefix = Gitlab::Application.config.assets.prefix
-      digest = Gitlab::Application.config.assets.digest
-
-      JSON.parse(File.read(path)).map do |hash|
-        if digest
-          fname = "#{hash['unicode']}-#{hash['digest']}"
-        else
-          fname = hash['unicode']
-        end
-
-        { name: hash['name'], path: "#{prefix}/#{fname}.png" }
-      end
-    end
-  end
-end
diff --git a/lib/backup/database.rb b/lib/backup/database.rb
index 67b2a64bd103d9a602a8f54f60b3564d9aed284f..22319ec6623f4379c15ab074ff3e74082328f9d9 100644
--- a/lib/backup/database.rb
+++ b/lib/backup/database.rb
@@ -86,9 +86,9 @@ module Backup
 
     def report_success(success)
       if success
-        $progress.puts '[DONE]'.green
+        $progress.puts '[DONE]'.color(:green)
       else
-        $progress.puts '[FAILED]'.red
+        $progress.puts '[FAILED]'.color(:red)
       end
     end
   end
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index 5a04201aadf9f319962323ab2e2491d9ffc4d8fa..e47ca01862b642b107095399a2d036e33593bb1f 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -1,5 +1,8 @@
 module Backup
   class Manager
+    ARCHIVES_TO_BACKUP = %w[uploads builds artifacts pages lfs registry]
+    FOLDERS_TO_BACKUP = %w[repositories db]
+
     def pack
       # Make sure there is a connection
       ActiveRecord::Base.connection.reconnect!
@@ -24,9 +27,9 @@ module Backup
         # Set file permissions on open to prevent chmod races.
         tar_system_options = {out: [tar_file, 'w', Gitlab.config.backup.archive_permissions]}
         if Kernel.system('tar', '-cf', '-', *backup_contents, tar_system_options)
-          $progress.puts "done".green
+          $progress.puts "done".color(:green)
         else
-          puts "creating archive #{tar_file} failed".red
+          puts "creating archive #{tar_file} failed".color(:red)
           abort 'Backup failed'
         end
 
@@ -35,24 +38,22 @@ module Backup
     end
 
     def upload(tar_file)
-      remote_directory = Gitlab.config.backup.upload.remote_directory
       $progress.print "Uploading backup archive to remote storage #{remote_directory} ... "
 
       connection_settings = Gitlab.config.backup.upload.connection
       if connection_settings.blank?
-        $progress.puts "skipped".yellow
+        $progress.puts "skipped".color(:yellow)
         return
       end
 
-      connection = ::Fog::Storage.new(connection_settings)
-      directory = connection.directories.get(remote_directory)
+      directory = connect_to_remote_directory(connection_settings)
 
       if directory.files.create(key: tar_file, body: File.open(tar_file), public: false,
           multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size,
           encryption: Gitlab.config.backup.upload.encryption)
-        $progress.puts "done".green
+        $progress.puts "done".color(:green)
       else
-        puts "uploading backup to #{remote_directory} failed".red
+        puts "uploading backup to #{remote_directory} failed".color(:red)
         abort 'Backup failed'
       end
     end
@@ -64,9 +65,9 @@ module Backup
         next unless File.exist?(File.join(Gitlab.config.backup.path, dir))
 
         if FileUtils.rm_rf(File.join(Gitlab.config.backup.path, dir))
-          $progress.puts "done".green
+          $progress.puts "done".color(:green)
         else
-          puts "deleting tmp directory '#{dir}' failed".red
+          puts "deleting tmp directory '#{dir}' failed".color(:red)
           abort 'Backup failed'
         end
       end
@@ -92,9 +93,9 @@ module Backup
           end
         end
 
-        $progress.puts "done. (#{removed} removed)".green
+        $progress.puts "done. (#{removed} removed)".color(:green)
       else
-        $progress.puts "skipping".yellow
+        $progress.puts "skipping".color(:yellow)
       end
     end
 
@@ -121,20 +122,20 @@ module Backup
       $progress.print "Unpacking backup ... "
 
       unless Kernel.system(*%W(tar -xf #{tar_file}))
-        puts "unpacking backup failed".red
+        puts "unpacking backup failed".color(:red)
         exit 1
       else
-        $progress.puts "done".green
+        $progress.puts "done".color(:green)
       end
 
       ENV["VERSION"] = "#{settings[:db_version]}" if settings[:db_version].to_i > 0
 
       # restoring mismatching backups can lead to unexpected problems
       if settings[:gitlab_version] != Gitlab::VERSION
-        puts "GitLab version mismatch:".red
-        puts "  Your current GitLab version (#{Gitlab::VERSION}) differs from the GitLab version in the backup!".red
-        puts "  Please switch to the following version and try again:".red
-        puts "  version: #{settings[:gitlab_version]}".red
+        puts "GitLab version mismatch:".color(:red)
+        puts "  Your current GitLab version (#{Gitlab::VERSION}) differs from the GitLab version in the backup!".color(:red)
+        puts "  Please switch to the following version and try again:".color(:red)
+        puts "  version: #{settings[:gitlab_version]}".color(:red)
         puts
         puts "Hint: git checkout v#{settings[:gitlab_version]}"
         exit 1
@@ -147,21 +148,44 @@ module Backup
     end
 
     def skipped?(item)
-      settings[:skipped] && settings[:skipped].include?(item)
+      settings[:skipped] && settings[:skipped].include?(item) || disabled_features.include?(item)
     end
 
     private
 
+    def connect_to_remote_directory(connection_settings)
+      connection = ::Fog::Storage.new(connection_settings)
+
+      # We only attempt to create the directory for local backups. For AWS
+      # and other cloud providers, we cannot guarantee the user will have
+      # permission to create the bucket.
+      if connection.service == ::Fog::Storage::Local
+        connection.directories.create(key: remote_directory)
+      else
+        connection.directories.get(remote_directory)
+      end
+    end
+
+    def remote_directory
+      Gitlab.config.backup.upload.remote_directory
+    end
+
     def backup_contents
       folders_to_backup + archives_to_backup + ["backup_information.yml"]
     end
 
     def archives_to_backup
-      %w{uploads builds artifacts pages lfs registry}.map{ |name| (name + ".tar.gz") unless skipped?(name) }.compact
+      ARCHIVES_TO_BACKUP.map{ |name| (name + ".tar.gz") unless skipped?(name) }.compact
     end
 
     def folders_to_backup
-      %w{repositories db}.reject{ |name| skipped?(name) }
+      FOLDERS_TO_BACKUP.reject{ |name| skipped?(name) }
+    end
+
+    def disabled_features
+      features = []
+      features << 'registry' unless Gitlab.config.registry.enabled
+      features
     end
 
     def settings
diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb
index a82a7e1f7bf70e51f90c112015941b68913587ff..7b91215d50b6f17118917404ae5a01089bdd5bd1 100644
--- a/lib/backup/repository.rb
+++ b/lib/backup/repository.rb
@@ -14,14 +14,14 @@ module Backup
         FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.path)) if project.namespace
 
         if project.empty_repo?
-          $progress.puts "[SKIPPED]".cyan
+          $progress.puts "[SKIPPED]".color(:cyan)
         else
           cmd = %W(tar -cf #{path_to_bundle(project)} -C #{path_to_repo(project)} .)
           output, status = Gitlab::Popen.popen(cmd)
           if status.zero?
-            $progress.puts "[DONE]".green
+            $progress.puts "[DONE]".color(:green)
           else
-            puts "[FAILED]".red
+            puts "[FAILED]".color(:red)
             puts "failed: #{cmd.join(' ')}"
             puts output
             abort 'Backup failed'
@@ -33,14 +33,14 @@ module Backup
         if File.exists?(path_to_repo(wiki))
           $progress.print " * #{wiki.path_with_namespace} ... "
           if wiki.repository.empty?
-            $progress.puts " [SKIPPED]".cyan
+            $progress.puts " [SKIPPED]".color(:cyan)
           else
             cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{path_to_repo(wiki)} bundle create #{path_to_bundle(wiki)} --all)
             output, status = Gitlab::Popen.popen(cmd)
             if status.zero?
-              $progress.puts " [DONE]".green
+              $progress.puts " [DONE]".color(:green)
             else
-              puts " [FAILED]".red
+              puts " [FAILED]".color(:red)
               puts "failed: #{cmd.join(' ')}"
               abort 'Backup failed'
             end
@@ -71,9 +71,9 @@ module Backup
         end
 
         if system(*cmd, silent)
-          $progress.puts "[DONE]".green
+          $progress.puts "[DONE]".color(:green)
         else
-          puts "[FAILED]".red
+          puts "[FAILED]".color(:red)
           puts "failed: #{cmd.join(' ')}"
           abort 'Restore failed'
         end
@@ -90,21 +90,21 @@ module Backup
           cmd = %W(#{Gitlab.config.git.bin_path} clone --bare #{path_to_bundle(wiki)} #{path_to_repo(wiki)})
 
           if system(*cmd, silent)
-            $progress.puts " [DONE]".green
+            $progress.puts " [DONE]".color(:green)
           else
-            puts " [FAILED]".red
+            puts " [FAILED]".color(:red)
             puts "failed: #{cmd.join(' ')}"
             abort 'Restore failed'
           end
         end
       end
 
-      $progress.print 'Put GitLab hooks in repositories dirs'.yellow
+      $progress.print 'Put GitLab hooks in repositories dirs'.color(:yellow)
       cmd = "#{Gitlab.config.gitlab_shell.path}/bin/create-hooks"
       if system(cmd)
-        $progress.puts " [DONE]".green
+        $progress.puts " [DONE]".color(:green)
       else
-        puts " [FAILED]".red
+        puts " [FAILED]".color(:red)
         puts "failed: #{cmd}"
       end
 
diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
index b8962379cb57122826e04141f2114f6961a153bd..db95d7c908b5232d7e218a2221498b62cc1b530e 100644
--- a/lib/banzai/filter/abstract_reference_filter.rb
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -18,10 +18,6 @@ module Banzai
         @object_sym ||= object_name.to_sym
       end
 
-      def self.data_reference
-        @data_reference ||= "data-#{object_name.dasherize}"
-      end
-
       def self.object_class_title
         @object_title ||= object_class.name.titleize
       end
@@ -45,10 +41,6 @@ module Banzai
         end
       end
 
-      def self.referenced_by(node)
-        { object_sym => LazyReference.new(object_class, node.attr(data_reference)) }
-      end
-
       def object_class
         self.class.object_class
       end
@@ -236,7 +228,9 @@ module Banzai
         if cache.key?(key)
           cache[key]
         else
-          cache[key] = yield
+          value = yield
+          cache[key] = value if key.present?
+          value
         end
       end
     end
diff --git a/lib/banzai/filter/commit_range_reference_filter.rb b/lib/banzai/filter/commit_range_reference_filter.rb
index b469ea0f62665130e8d3838d829edbc8899108fb..bbb88c979cc3f3c6e4d8d6895bf5e9262666364a 100644
--- a/lib/banzai/filter/commit_range_reference_filter.rb
+++ b/lib/banzai/filter/commit_range_reference_filter.rb
@@ -4,6 +4,8 @@ module Banzai
     #
     # This filter supports cross-project references.
     class CommitRangeReferenceFilter < AbstractReferenceFilter
+      self.reference_type = :commit_range
+
       def self.object_class
         CommitRange
       end
@@ -14,34 +16,18 @@ module Banzai
         end
       end
 
-      def self.referenced_by(node)
-        project = Project.find(node.attr("data-project")) rescue nil
-        return unless project
-
-        id = node.attr("data-commit-range")
-        range = find_object(project, id)
-
-        return unless range
-
-        { commit_range: range }
-      end
-
       def initialize(*args)
         super
 
         @commit_map = {}
       end
 
-      def self.find_object(project, id)
+      def find_object(project, id)
         range = CommitRange.new(id, project)
 
         range.valid_commits? ? range : nil
       end
 
-      def find_object(*args)
-        self.class.find_object(*args)
-      end
-
       def url_for_object(range, project)
         h = Gitlab::Routing.url_helpers
         h.namespace_project_compare_url(project.namespace, project,
diff --git a/lib/banzai/filter/commit_reference_filter.rb b/lib/banzai/filter/commit_reference_filter.rb
index bd88207326ca31c8c0bafa227dd667a0671a1cd5..2ce1816672b93e630244e0048675e4511ff1e56f 100644
--- a/lib/banzai/filter/commit_reference_filter.rb
+++ b/lib/banzai/filter/commit_reference_filter.rb
@@ -4,6 +4,8 @@ module Banzai
     #
     # This filter supports cross-project references.
     class CommitReferenceFilter < AbstractReferenceFilter
+      self.reference_type = :commit
+
       def self.object_class
         Commit
       end
@@ -14,28 +16,12 @@ module Banzai
         end
       end
 
-      def self.referenced_by(node)
-        project = Project.find(node.attr("data-project")) rescue nil
-        return unless project
-
-        id = node.attr("data-commit")
-        commit = find_object(project, id)
-
-        return unless commit
-
-        { commit: commit }
-      end
-
-      def self.find_object(project, id)
+      def find_object(project, id)
         if project && project.valid_repo?
           project.commit(id)
         end
       end
 
-      def find_object(*args)
-        self.class.find_object(*args)
-      end
-
       def url_for_object(commit, project)
         h = Gitlab::Routing.url_helpers
         h.namespace_project_commit_url(project.namespace, project, commit,
diff --git a/lib/banzai/filter/external_issue_reference_filter.rb b/lib/banzai/filter/external_issue_reference_filter.rb
index 37344b9057658b6487c5e3a40e3c12a933d6775d..eaa702952ccbeca8b4fc7c76e698ce0a7b9776cf 100644
--- a/lib/banzai/filter/external_issue_reference_filter.rb
+++ b/lib/banzai/filter/external_issue_reference_filter.rb
@@ -4,6 +4,8 @@ module Banzai
     # References are ignored if the project doesn't use an external issue
     # tracker.
     class ExternalIssueReferenceFilter < ReferenceFilter
+      self.reference_type = :external_issue
+
       # Public: Find `JIRA-123` issue references in text
       #
       #   ExternalIssueReferenceFilter.references_in(text) do |match, issue|
@@ -21,18 +23,6 @@ module Banzai
         end
       end
 
-      def self.referenced_by(node)
-        project = Project.find(node.attr("data-project")) rescue nil
-        return unless project
-
-        id = node.attr("data-external-issue")
-        external_issue = ExternalIssue.new(id, project)
-
-        return unless external_issue
-
-        { external_issue: external_issue }
-      end
-
       def call
         # Early return if the project isn't using an external tracker
         return doc if project.nil? || default_issues_tracker?
diff --git a/lib/banzai/filter/external_link_filter.rb b/lib/banzai/filter/external_link_filter.rb
index 38c4219518e52b811105affb7805057a34001876..f73ecfc94184900a62d7e8466efac9f6a507f861 100644
--- a/lib/banzai/filter/external_link_filter.rb
+++ b/lib/banzai/filter/external_link_filter.rb
@@ -15,6 +15,7 @@ module Banzai
           next if link.start_with?(internal_url)
 
           node.set_attribute('rel', 'nofollow noreferrer')
+          node.set_attribute('target', '_blank')
         end
 
         doc
diff --git a/lib/banzai/filter/inline_diff_filter.rb b/lib/banzai/filter/inline_diff_filter.rb
index 9e75edd4d4c46e48cb90e8176fc19a377f6ffdf6..beb21b19ab3b60f075b038b23a6728380174e6ce 100644
--- a/lib/banzai/filter/inline_diff_filter.rb
+++ b/lib/banzai/filter/inline_diff_filter.rb
@@ -8,15 +8,19 @@ module Banzai
           next if has_ancestor?(node, IGNORED_ANCESTOR_TAGS)
 
           content = node.to_html
-          content = content.gsub(/(?:\[\-(.*?)\-\]|\{\-(.*?)\-\})/, '<span class="idiff left right deletion">\1\2</span>')
-          content = content.gsub(/(?:\[\+(.*?)\+\]|\{\+(.*?)\+\})/, '<span class="idiff left right addition">\1\2</span>')
+          html_content = inline_diff_filter(content)
 
-          next if html == content
+          next if content == html_content
 
-          node.replace(content)
+          node.replace(html_content)
         end
         doc
       end
+
+      def inline_diff_filter(text)
+        html_content = text.gsub(/(?:\[\-(.*?)\-\]|\{\-(.*?)\-\})/, '<span class="idiff left right deletion">\1\2</span>')
+        html_content.gsub(/(?:\[\+(.*?)\+\]|\{\+(.*?)\+\})/, '<span class="idiff left right addition">\1\2</span>')
+      end
     end
   end
 end
diff --git a/lib/banzai/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb
index 59c5e89c5466e5f054e58e0f00124d0cfb435235..2496e704002a29a8f2a2d5d6ee4128d5bf3b4934 100644
--- a/lib/banzai/filter/issue_reference_filter.rb
+++ b/lib/banzai/filter/issue_reference_filter.rb
@@ -5,18 +5,12 @@ module Banzai
     #
     # This filter supports cross-project references.
     class IssueReferenceFilter < AbstractReferenceFilter
+      self.reference_type = :issue
+
       def self.object_class
         Issue
       end
 
-      def self.user_can_see_reference?(user, node, context)
-        # It is not possible to check access rights for external issue trackers
-        return true if context[:project].try(:external_issue_tracker)
- 
-        issue = Issue.find(node.attr('data-issue')) rescue nil
-        Ability.abilities.allowed?(user, :read_issue, issue)
-      end
-
       def find_object(project, id)
         project.get_issue(id)
       end
diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb
index 8488a493b55cfd8df648b3d76b32a5cbc36e8b9a..e4d3f87d0aa77d5e01a86ab0e8d3aa7e59d09e58 100644
--- a/lib/banzai/filter/label_reference_filter.rb
+++ b/lib/banzai/filter/label_reference_filter.rb
@@ -2,6 +2,8 @@ module Banzai
   module Filter
     # HTML filter that replaces label references with links.
     class LabelReferenceFilter < AbstractReferenceFilter
+      self.reference_type = :label
+
       def self.object_class
         Label
       end
diff --git a/lib/banzai/filter/merge_request_reference_filter.rb b/lib/banzai/filter/merge_request_reference_filter.rb
index cad38a51851f6b405e978c5194c767ea44061e71..ac5216d9cfb3d755034dd5d06b5b3d2dc25a5cfc 100644
--- a/lib/banzai/filter/merge_request_reference_filter.rb
+++ b/lib/banzai/filter/merge_request_reference_filter.rb
@@ -5,6 +5,8 @@ module Banzai
     #
     # This filter supports cross-project references.
     class MergeRequestReferenceFilter < AbstractReferenceFilter
+      self.reference_type = :merge_request
+
       def self.object_class
         MergeRequest
       end
diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb
index dad0768f51b74b6020ff1e6cfdadb0f72cc7c588..ca686c87d97bd9ef628c40b7d525c8cebcefe608 100644
--- a/lib/banzai/filter/milestone_reference_filter.rb
+++ b/lib/banzai/filter/milestone_reference_filter.rb
@@ -2,6 +2,8 @@ module Banzai
   module Filter
     # HTML filter that replaces milestone references with links.
     class MilestoneReferenceFilter < AbstractReferenceFilter
+      self.reference_type = :milestone
+
       def self.object_class
         Milestone
       end
diff --git a/lib/banzai/filter/redactor_filter.rb b/lib/banzai/filter/redactor_filter.rb
index e589b5df6ec62689bfde24ba98ae7bf6686be1bd..c753a84a20d9a2d556cfc857d46f8f507d6ce2b4 100644
--- a/lib/banzai/filter/redactor_filter.rb
+++ b/lib/banzai/filter/redactor_filter.rb
@@ -7,8 +7,11 @@ module Banzai
     #
     class RedactorFilter < HTML::Pipeline::Filter
       def call
-        Querying.css(doc, 'a.gfm').each do |node|
-          unless user_can_see_reference?(node)
+        nodes = Querying.css(doc, 'a.gfm[data-reference-type]')
+        visible = nodes_visible_to_user(nodes)
+
+        nodes.each do |node|
+          unless visible.include?(node)
             # The reference should be replaced by the original text,
             # which is not always the same as the rendered text.
             text = node.attr('data-original') || node.text
@@ -21,20 +24,30 @@ module Banzai
 
       private
 
-      def user_can_see_reference?(node)
-        if node.has_attribute?('data-reference-filter')
-          reference_type = node.attr('data-reference-filter')
-          reference_filter = Banzai::Filter.const_get(reference_type)
+      def nodes_visible_to_user(nodes)
+        per_type = Hash.new { |h, k| h[k] = [] }
+        visible = Set.new
+
+        nodes.each do |node|
+          per_type[node.attr('data-reference-type')] << node
+        end
+
+        per_type.each do |type, nodes|
+          parser = Banzai::ReferenceParser[type].new(project, current_user)
 
-          reference_filter.user_can_see_reference?(current_user, node, context)
-        else
-          true
+          visible.merge(parser.nodes_visible_to_user(current_user, nodes))
         end
+
+        visible
       end
 
       def current_user
         context[:current_user]
       end
+
+      def project
+        context[:project]
+      end
     end
   end
 end
diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb
index 31386cf851c70050d16fbcbc9c9f5a2f2864cd0f..2d6f34c9cd8633fec321e97241b5e556e9981765 100644
--- a/lib/banzai/filter/reference_filter.rb
+++ b/lib/banzai/filter/reference_filter.rb
@@ -8,24 +8,8 @@ module Banzai
     #   :project (required) - Current project, ignored if reference is cross-project.
     #   :only_path          - Generate path-only links.
     class ReferenceFilter < HTML::Pipeline::Filter
-      def self.user_can_see_reference?(user, node, context)
-        if node.has_attribute?('data-project')
-          project_id = node.attr('data-project').to_i
-          return true if project_id == context[:project].try(:id)
-
-          project = Project.find(project_id) rescue nil
-          Ability.abilities.allowed?(user, :read_project, project)
-        else
-          true
-        end
-      end
-
-      def self.user_can_reference?(user, node, context)
-        true
-      end
-
-      def self.referenced_by(node)
-        raise NotImplementedError, "#{self} does not implement #{__method__}"
+      class << self
+        attr_accessor :reference_type
       end
 
       # Returns a data attribute String to attach to a reference link
@@ -43,7 +27,9 @@ module Banzai
       #
       # Returns a String
       def data_attribute(attributes = {})
-        attributes[:reference_filter] = self.class.name.demodulize
+        attributes = attributes.reject { |_, v| v.nil? }
+
+        attributes[:reference_type] = self.class.reference_type
         attributes.delete(:original) if context[:no_original_data]
         attributes.map { |key, value| %Q(data-#{key.to_s.dasherize}="#{escape_once(value)}") }.join(" ")
       end
@@ -82,6 +68,8 @@ module Banzai
       # by `ignore_ancestor_query`. Link tags are not processed if they have a
       # "gfm" class or the "href" attribute is empty.
       def each_node
+        return to_enum(__method__) unless block_given?
+
         query = %Q{descendant-or-self::text()[not(#{ignore_ancestor_query})]
         | descendant-or-self::a[
           not(contains(concat(" ", @class, " "), " gfm ")) and not(@href = "")
@@ -92,6 +80,11 @@ module Banzai
         end
       end
 
+      # Returns an Array containing all HTML nodes.
+      def nodes
+        @nodes ||= each_node.to_a
+      end
+
       # Yields the link's URL and text whenever the node is a valid <a> tag.
       def yield_valid_link(node)
         link = CGI.unescape(node.attr('href').to_s)
diff --git a/lib/banzai/filter/reference_gatherer_filter.rb b/lib/banzai/filter/reference_gatherer_filter.rb
deleted file mode 100644
index 96fdb06304e5f6f188cf80bf124427250532738d..0000000000000000000000000000000000000000
--- a/lib/banzai/filter/reference_gatherer_filter.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-module Banzai
-  module Filter
-    # HTML filter that gathers all referenced records that the current user has
-    # permission to view.
-    #
-    # Expected to be run in its own post-processing pipeline.
-    #
-    class ReferenceGathererFilter < HTML::Pipeline::Filter
-      def initialize(*)
-        super
-
-        result[:references] ||= Hash.new { |hash, type| hash[type] = [] }
-      end
-
-      def call
-        Querying.css(doc, 'a.gfm').each do |node|
-          gather_references(node)
-        end
-
-        load_lazy_references unless ReferenceExtractor.lazy?
-
-        doc
-      end
-
-      private
-
-      def gather_references(node)
-        return unless node.has_attribute?('data-reference-filter')
-
-        reference_type = node.attr('data-reference-filter')
-        reference_filter = Banzai::Filter.const_get(reference_type)
-
-        return if context[:reference_filter] && reference_filter != context[:reference_filter]
-
-        return if author && !reference_filter.user_can_reference?(author, node, context)
-
-        return unless reference_filter.user_can_see_reference?(current_user, node, context)
-
-        references = reference_filter.referenced_by(node)
-        return unless references
-
-        references.each do |type, values|
-          Array.wrap(values).each do |value|
-            result[:references][type] << value
-          end
-        end
-      end
-
-      def load_lazy_references
-        refs = result[:references]
-        refs.each do |type, values|
-          refs[type] = ReferenceExtractor.lazily(values)
-        end
-      end
-
-      def current_user
-        context[:current_user]
-      end
-
-      def author
-        context[:author]
-      end
-    end
-  end
-end
diff --git a/lib/banzai/filter/snippet_reference_filter.rb b/lib/banzai/filter/snippet_reference_filter.rb
index d507eb5ebe1f608bf86d2a4ffb86536f3fda9b0c..212a0bbf2a03f775e01509a9707f5440d7d9e5d5 100644
--- a/lib/banzai/filter/snippet_reference_filter.rb
+++ b/lib/banzai/filter/snippet_reference_filter.rb
@@ -5,6 +5,8 @@ module Banzai
     #
     # This filter supports cross-project references.
     class SnippetReferenceFilter < AbstractReferenceFilter
+      self.reference_type = :snippet
+
       def self.object_class
         Snippet
       end
diff --git a/lib/banzai/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb
index eea3af842b6e10b5be98112a543ccdf638c72c9e..5b0a6d8541b0edd636394ee64f9e3d3de817dc11 100644
--- a/lib/banzai/filter/user_reference_filter.rb
+++ b/lib/banzai/filter/user_reference_filter.rb
@@ -4,6 +4,8 @@ module Banzai
     #
     # A special `@all` reference is also supported.
     class UserReferenceFilter < ReferenceFilter
+      self.reference_type = :user
+
       # Public: Find `@user` user references in text
       #
       #   UserReferenceFilter.references_in(text) do |match, username|
@@ -21,50 +23,13 @@ module Banzai
         end
       end
 
-      def self.referenced_by(node)
-        if node.has_attribute?('data-group')
-          group = Group.find(node.attr('data-group')) rescue nil
-          return unless group
-
-          { user: group.users }
-        elsif node.has_attribute?('data-user')
-          { user: LazyReference.new(User, node.attr('data-user')) }
-        elsif node.has_attribute?('data-project')
-          project = Project.find(node.attr('data-project')) rescue nil
-          return unless project
-
-          { user: project.team.members.flatten }
-        end
-      end
-
-      def self.user_can_see_reference?(user, node, context)
-        if node.has_attribute?('data-group')
-          group = Group.find(node.attr('data-group')) rescue nil
-          Ability.abilities.allowed?(user, :read_group, group)
-        else
-          super
-        end
-      end
-
-      def self.user_can_reference?(user, node, context)
-        # Only team members can reference `@all`
-        if node.has_attribute?('data-project')
-          project = Project.find(node.attr('data-project')) rescue nil
-          return false unless project
-
-          user && project.team.member?(user)
-        else
-          super
-        end
-      end
-
       def call
         return doc if project.nil?
 
         ref_pattern = User.reference_pattern
         ref_pattern_start = /\A#{ref_pattern}\z/
 
-        each_node do |node|
+        nodes.each do |node|
           if text_node?(node)
             replace_text_when_pattern_matches(node, ref_pattern) do |content|
               user_link_filter(content)
@@ -94,7 +59,7 @@ module Banzai
         self.class.references_in(text) do |match, username|
           if username == 'all'
             link_to_all(link_text: link_text)
-          elsif namespace = Namespace.find_by(path: username)
+          elsif namespace = namespaces[username]
             link_to_namespace(namespace, link_text: link_text) || match
           else
             match
@@ -102,6 +67,31 @@ module Banzai
         end
       end
 
+      # Returns a Hash containing all Namespace objects for the username
+      # references in the current document.
+      #
+      # The keys of this Hash are the namespace paths, the values the
+      # corresponding Namespace objects.
+      def namespaces
+        @namespaces ||=
+          Namespace.where(path: usernames).each_with_object({}) do |row, hash|
+            hash[row.path] = row
+          end
+      end
+
+      # Returns all usernames referenced in the current document.
+      def usernames
+        refs = Set.new
+
+        nodes.each do |node|
+          node.to_html.scan(User.reference_pattern) do
+            refs << $~[:user]
+          end
+        end
+
+        refs.to_a
+      end
+
       private
 
       def urls
@@ -114,9 +104,12 @@ module Banzai
 
       def link_to_all(link_text: nil)
         project = context[:project]
+        author = context[:author]
+
         url = urls.namespace_project_url(project.namespace, project,
                                          only_path: context[:only_path])
-        data = data_attribute(project: project.id)
+
+        data = data_attribute(project: project.id, author: author.try(:id))
         text = link_text || User.reference_prefix + 'all'
 
         link_tag(url, data, text)
diff --git a/lib/banzai/filter/wiki_link_filter.rb b/lib/banzai/filter/wiki_link_filter.rb
index 7dc771afd71a51752c6e2e86920fcb43d8e49f99..37a2779d453ef397da4e04354486bd69f10d6acd 100644
--- a/lib/banzai/filter/wiki_link_filter.rb
+++ b/lib/banzai/filter/wiki_link_filter.rb
@@ -2,7 +2,8 @@ require 'uri'
 
 module Banzai
   module Filter
-    # HTML filter that "fixes" relative links to files in a repository.
+    # HTML filter that "fixes" links to pages/files in a wiki.
+    # Rewrite rules are documented in the `WikiPipeline` spec.
     #
     # Context options:
     #   :project_wiki
@@ -25,36 +26,15 @@ module Banzai
       end
 
       def process_link_attr(html_attr)
-        return if html_attr.blank? || file_reference?(html_attr) || hierarchical_link?(html_attr)
+        return if html_attr.blank?
 
-        uri = URI(html_attr.value)
-        if uri.relative? && uri.path.present?
-          html_attr.value = rebuild_wiki_uri(uri).to_s
-        end
+        html_attr.value = apply_rewrite_rules(html_attr.value)
       rescue URI::Error
         # noop
       end
 
-      def rebuild_wiki_uri(uri)
-        uri.path = ::File.join(project_wiki_base_path, uri.path)
-        uri
-      end
-
-      def project_wiki
-        context[:project_wiki]
-      end
-
-      def file_reference?(html_attr)
-        !File.extname(html_attr.value).blank?
-      end
-
-      # Of the form `./link`, `../link`, or similar
-      def hierarchical_link?(html_attr)
-        html_attr.value[0] == '.'
-      end
-
-      def project_wiki_base_path
-        project_wiki && project_wiki.wiki_base_path
+      def apply_rewrite_rules(link_string)
+        Rewriter.new(link_string, wiki: context[:project_wiki], slug: context[:page_slug]).apply_rules
       end
     end
   end
diff --git a/lib/banzai/filter/wiki_link_filter/rewriter.rb b/lib/banzai/filter/wiki_link_filter/rewriter.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2e2c8da311e433fccf970748061ce99366c1d028
--- /dev/null
+++ b/lib/banzai/filter/wiki_link_filter/rewriter.rb
@@ -0,0 +1,40 @@
+module Banzai
+  module Filter
+    class WikiLinkFilter < HTML::Pipeline::Filter
+      class Rewriter
+        def initialize(link_string, wiki:, slug:)
+          @uri = Addressable::URI.parse(link_string)
+          @wiki_base_path = wiki && wiki.wiki_base_path
+          @slug = slug
+        end
+
+        def apply_rules
+          apply_file_link_rules!
+          apply_hierarchical_link_rules!
+          apply_relative_link_rules!
+          @uri.to_s
+        end
+
+        private
+
+        # Of the form 'file.md'
+        def apply_file_link_rules!
+          @uri = Addressable::URI.join(@slug, @uri) if @uri.extname.present?
+        end
+
+        # Of the form `./link`, `../link`, or similar
+        def apply_hierarchical_link_rules!
+          @uri = Addressable::URI.join(@slug, @uri) if @uri.to_s[0] == '.'
+        end
+
+        # Any link _not_ of the form `http://example.com/`
+        def apply_relative_link_rules!
+          if @uri.relative? && @uri.path.present?
+            link = ::File.join(@wiki_base_path, @uri.path)
+            @uri = Addressable::URI.parse(link)
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/banzai/lazy_reference.rb b/lib/banzai/lazy_reference.rb
deleted file mode 100644
index 1095b4debc76b96c3a2950e42de32958f7d144e7..0000000000000000000000000000000000000000
--- a/lib/banzai/lazy_reference.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-module Banzai
-  class LazyReference
-    def self.load(refs)
-      lazy_references, values = refs.partition { |ref| ref.is_a?(self) }
-
-      lazy_values = lazy_references.group_by(&:klass).flat_map do |klass, refs|
-        ids = refs.flat_map(&:ids)
-        klass.where(id: ids)
-      end
-
-      values + lazy_values
-    end
-
-    attr_reader :klass, :ids
-
-    def initialize(klass, ids)
-      @klass = klass
-      @ids = Array.wrap(ids).map(&:to_i)
-    end
-
-    def load
-      self.klass.where(id: self.ids)
-    end
-  end
-end
diff --git a/lib/banzai/pipeline/description_pipeline.rb b/lib/banzai/pipeline/description_pipeline.rb
index f23958676585b66147ebab9b4e064073c15dc180..042fb2e6e1445dc58226556297099071c6d1d424 100644
--- a/lib/banzai/pipeline/description_pipeline.rb
+++ b/lib/banzai/pipeline/description_pipeline.rb
@@ -1,23 +1,16 @@
 module Banzai
   module Pipeline
     class DescriptionPipeline < FullPipeline
+      WHITELIST = Banzai::Filter::SanitizationFilter::LIMITED.deep_dup.merge(
+        elements: Banzai::Filter::SanitizationFilter::LIMITED[:elements] - %w(pre code img ol ul li)
+      )
+
       def self.transform_context(context)
         super(context).merge(
           # SanitizationFilter
-          whitelist: whitelist
+          whitelist: WHITELIST
         )
       end
-
-      private
-
-      def self.whitelist
-        # Descriptions are more heavily sanitized, allowing only a few elements.
-        # See http://git.io/vkuAN
-        whitelist = Banzai::Filter::SanitizationFilter::LIMITED
-        whitelist[:elements] -= %w(pre code img ol ul li)
-
-        whitelist
-      end
     end
   end
 end
diff --git a/lib/banzai/pipeline/reference_extraction_pipeline.rb b/lib/banzai/pipeline/reference_extraction_pipeline.rb
deleted file mode 100644
index 919998380e4a0ee6a02b618bcfe3230042745357..0000000000000000000000000000000000000000
--- a/lib/banzai/pipeline/reference_extraction_pipeline.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module Banzai
-  module Pipeline
-    class ReferenceExtractionPipeline < BasePipeline
-      def self.filters
-        FilterArray[
-          Filter::ReferenceGathererFilter
-        ]
-      end
-    end
-  end
-end
diff --git a/lib/banzai/reference_extractor.rb b/lib/banzai/reference_extractor.rb
index f4079538ec55d9ea88752607318e7767b6a6c233..bf366962aef318b2f9ee96756d0c5b504163c581 100644
--- a/lib/banzai/reference_extractor.rb
+++ b/lib/banzai/reference_extractor.rb
@@ -1,28 +1,6 @@
 module Banzai
   # Extract possible GFM references from an arbitrary String for further processing.
   class ReferenceExtractor
-    class << self
-      LAZY_KEY = :banzai_reference_extractor_lazy
-
-      def lazy?
-        Thread.current[LAZY_KEY]
-      end
-
-      def lazily(values = nil, &block)
-        return (values || block.call).uniq if lazy?
-
-        begin
-          Thread.current[LAZY_KEY] = true
-
-          values ||= block.call
-
-          Banzai::LazyReference.load(values.uniq).uniq
-        ensure
-          Thread.current[LAZY_KEY] = false
-        end
-      end
-    end
-
     def initialize
       @texts = []
     end
@@ -31,23 +9,21 @@ module Banzai
       @texts << Renderer.render(text, context)
     end
 
-    def references(type, context = {})
-      filter = Banzai::Filter["#{type}_reference"]
+    def references(type, project, current_user = nil)
+      processor = Banzai::ReferenceParser[type].
+        new(project, current_user)
+
+      processor.process(html_documents)
+    end
 
-      context.merge!(
-        pipeline: :reference_extraction,
+    private
 
-        # ReferenceGathererFilter
-        reference_filter: filter
-      )
+    def html_documents
+      # This ensures that we don't memoize anything until we have a number of
+      # text blobs to parse.
+      return [] if @texts.empty?
 
-      self.class.lazily do
-        @texts.flat_map do |html|
-          text_context = context.dup
-          result = Renderer.render_result(html, text_context)
-          result[:references][type]
-        end.uniq
-      end
+      @html_documents ||= @texts.map { |html| Nokogiri::HTML.fragment(html) }
     end
   end
 end
diff --git a/lib/banzai/reference_parser.rb b/lib/banzai/reference_parser.rb
new file mode 100644
index 0000000000000000000000000000000000000000..557bec4316e45fc10856799e54d919f5fb776be7
--- /dev/null
+++ b/lib/banzai/reference_parser.rb
@@ -0,0 +1,14 @@
+module Banzai
+  module ReferenceParser
+    # Returns the reference parser class for the given type
+    #
+    # Example:
+    #
+    #     Banzai::ReferenceParser['issue']
+    #
+    # This would return the `Banzai::ReferenceParser::IssueParser` class.
+    def self.[](name)
+      const_get("#{name.to_s.camelize}Parser")
+    end
+  end
+end
diff --git a/lib/banzai/reference_parser/base_parser.rb b/lib/banzai/reference_parser/base_parser.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3d7b9c4a02409cedd60995b73ca01f6542e90652
--- /dev/null
+++ b/lib/banzai/reference_parser/base_parser.rb
@@ -0,0 +1,204 @@
+module Banzai
+  module ReferenceParser
+    # Base class for reference parsing classes.
+    #
+    # Each parser should also specify its reference type by calling
+    # `self.reference_type = ...` in the body of the class. The value of this
+    # method should be a symbol such as `:issue` or `:merge_request`. For
+    # example:
+    #
+    #     class IssueParser < BaseParser
+    #       self.reference_type = :issue
+    #     end
+    #
+    # The reference type is used to determine what nodes to pass to the
+    # `referenced_by` method.
+    #
+    # Parser classes should either implement the instance method
+    # `references_relation` or overwrite `referenced_by`. The
+    # `references_relation` method is supposed to return an
+    # ActiveRecord::Relation used as a base relation for retrieving the objects
+    # referenced in a set of HTML nodes.
+    #
+    # Each class can implement two additional methods:
+    #
+    # * `nodes_user_can_reference`: returns an Array of nodes the given user can
+    #   refer to.
+    # * `nodes_visible_to_user`: returns an Array of nodes that are visible to
+    #   the given user.
+    #
+    # You only need to overwrite these methods if you want to tweak who can see
+    # which references. For example, the IssueParser class defines its own
+    # `nodes_visible_to_user` method so it can ensure users can only see issues
+    # they have access to.
+    class BaseParser
+      class << self
+        attr_accessor :reference_type
+      end
+
+      # Returns the attribute name containing the value for every object to be
+      # parsed by the current parser.
+      #
+      # For example, for a parser class that returns "Animal" objects this
+      # attribute would be "data-animal".
+      def self.data_attribute
+        @data_attribute ||= "data-#{reference_type.to_s.dasherize}"
+      end
+
+      def initialize(project = nil, current_user = nil)
+        @project = project
+        @current_user = current_user
+      end
+
+      # Returns all the nodes containing references that the user can refer to.
+      def nodes_user_can_reference(user, nodes)
+        nodes
+      end
+
+      # Returns all the nodes that are visible to the given user.
+      def nodes_visible_to_user(user, nodes)
+        projects = lazy { projects_for_nodes(nodes) }
+        project_attr = 'data-project'
+
+        nodes.select do |node|
+          if node.has_attribute?(project_attr)
+            node_id = node.attr(project_attr).to_i
+
+            if project && project.id == node_id
+              true
+            else
+              can?(user, :read_project, projects[node_id])
+            end
+          else
+            true
+          end
+        end
+      end
+
+      # Returns an Array of objects referenced by any of the given HTML nodes.
+      def referenced_by(nodes)
+        ids = unique_attribute_values(nodes, self.class.data_attribute)
+
+        references_relation.where(id: ids)
+      end
+
+      # Returns the ActiveRecord::Relation to use for querying references in the
+      # DB.
+      def references_relation
+        raise NotImplementedError,
+          "#{self.class} does not implement #{__method__}"
+      end
+
+      # Returns a Hash containing attribute values per project ID.
+      #
+      # The returned Hash uses the following format:
+      #
+      #     { project id => [value1, value2, ...] }
+      #
+      # nodes - An Array of HTML nodes to process.
+      # attribute - The name of the attribute (as a String) for which to gather
+      #             values.
+      #
+      # Returns a Hash.
+      def gather_attributes_per_project(nodes, attribute)
+        per_project = Hash.new { |hash, key| hash[key] = Set.new }
+
+        nodes.each do |node|
+          project_id = node.attr('data-project').to_i
+          id = node.attr(attribute)
+
+          per_project[project_id] << id if id
+        end
+
+        per_project
+      end
+
+      # Returns a Hash containing objects for an attribute grouped per their
+      # IDs.
+      #
+      # The returned Hash uses the following format:
+      #
+      #     { id value => row }
+      #
+      # nodes - An Array of HTML nodes to process.
+      #
+      # collection - The model or ActiveRecord relation to use for retrieving
+      #              rows from the database.
+      #
+      # attribute - The name of the attribute containing the primary key values
+      #             for every row.
+      #
+      # Returns a Hash.
+      def grouped_objects_for_nodes(nodes, collection, attribute)
+        return {} if nodes.empty?
+
+        ids = unique_attribute_values(nodes, attribute)
+
+        collection.where(id: ids).each_with_object({}) do |row, hash|
+          hash[row.id] = row
+        end
+      end
+
+      # Returns an Array containing all unique values of an attribute of the
+      # given nodes.
+      def unique_attribute_values(nodes, attribute)
+        values = Set.new
+
+        nodes.each do |node|
+          if node.has_attribute?(attribute)
+            values << node.attr(attribute)
+          end
+        end
+
+        values.to_a
+      end
+
+      # Processes the list of HTML documents and returns an Array containing all
+      # the references.
+      def process(documents)
+        type = self.class.reference_type
+
+        nodes = documents.flat_map do |document|
+          Querying.css(document, "a[data-reference-type='#{type}'].gfm").to_a
+        end
+
+        gather_references(nodes)
+      end
+
+      # Gathers the references for the given HTML nodes.
+      def gather_references(nodes)
+        nodes = nodes_user_can_reference(current_user, nodes)
+        nodes = nodes_visible_to_user(current_user, nodes)
+
+        referenced_by(nodes)
+      end
+
+      # Returns a Hash containing the projects for a given list of HTML nodes.
+      #
+      # The returned Hash uses the following format:
+      #
+      #     { project ID => project }
+      #
+      def projects_for_nodes(nodes)
+        @projects_for_nodes ||=
+          grouped_objects_for_nodes(nodes, Project, 'data-project')
+      end
+
+      def can?(user, permission, subject)
+        Ability.abilities.allowed?(user, permission, subject)
+      end
+
+      def find_projects_for_hash_keys(hash)
+        Project.where(id: hash.keys)
+      end
+
+      private
+
+      attr_reader :current_user, :project
+
+      def lazy(&block)
+        Gitlab::Lazy.new(&block)
+      end
+    end
+  end
+end
diff --git a/lib/banzai/reference_parser/commit_parser.rb b/lib/banzai/reference_parser/commit_parser.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0fee9d267dee639dea5682204e292edb950317d7
--- /dev/null
+++ b/lib/banzai/reference_parser/commit_parser.rb
@@ -0,0 +1,34 @@
+module Banzai
+  module ReferenceParser
+    class CommitParser < BaseParser
+      self.reference_type = :commit
+
+      def referenced_by(nodes)
+        commit_ids = commit_ids_per_project(nodes)
+        projects = find_projects_for_hash_keys(commit_ids)
+
+        projects.flat_map do |project|
+          find_commits(project, commit_ids[project.id])
+        end
+      end
+
+      def commit_ids_per_project(nodes)
+        gather_attributes_per_project(nodes, self.class.data_attribute)
+      end
+
+      def find_commits(project, ids)
+        commits = []
+
+        return commits unless project.valid_repo?
+
+        ids.each do |id|
+          commit = project.commit(id)
+
+          commits << commit if commit
+        end
+
+        commits
+      end
+    end
+  end
+end
diff --git a/lib/banzai/reference_parser/commit_range_parser.rb b/lib/banzai/reference_parser/commit_range_parser.rb
new file mode 100644
index 0000000000000000000000000000000000000000..69d01f8db1577c67ecaf7cfe4a5880f57688bdbb
--- /dev/null
+++ b/lib/banzai/reference_parser/commit_range_parser.rb
@@ -0,0 +1,38 @@
+module Banzai
+  module ReferenceParser
+    class CommitRangeParser < BaseParser
+      self.reference_type = :commit_range
+
+      def referenced_by(nodes)
+        range_ids = commit_range_ids_per_project(nodes)
+        projects = find_projects_for_hash_keys(range_ids)
+
+        projects.flat_map do |project|
+          find_ranges(project, range_ids[project.id])
+        end
+      end
+
+      def commit_range_ids_per_project(nodes)
+        gather_attributes_per_project(nodes, self.class.data_attribute)
+      end
+
+      def find_ranges(project, range_ids)
+        ranges = []
+
+        range_ids.each do |id|
+          range = find_object(project, id)
+
+          ranges << range if range
+        end
+
+        ranges
+      end
+
+      def find_object(project, id)
+        range = CommitRange.new(id, project)
+
+        range.valid_commits? ? range : nil
+      end
+    end
+  end
+end
diff --git a/lib/banzai/reference_parser/external_issue_parser.rb b/lib/banzai/reference_parser/external_issue_parser.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a1264db21115f0ff19d3997e8cd7ad41337881c1
--- /dev/null
+++ b/lib/banzai/reference_parser/external_issue_parser.rb
@@ -0,0 +1,25 @@
+module Banzai
+  module ReferenceParser
+    class ExternalIssueParser < BaseParser
+      self.reference_type = :external_issue
+
+      def referenced_by(nodes)
+        issue_ids = issue_ids_per_project(nodes)
+        projects = find_projects_for_hash_keys(issue_ids)
+        issues = []
+
+        projects.each do |project|
+          issue_ids[project.id].each do |id|
+            issues << ExternalIssue.new(id, project)
+          end
+        end
+
+        issues
+      end
+
+      def issue_ids_per_project(nodes)
+        gather_attributes_per_project(nodes, self.class.data_attribute)
+      end
+    end
+  end
+end
diff --git a/lib/banzai/reference_parser/issue_parser.rb b/lib/banzai/reference_parser/issue_parser.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f306079d833dfc231c51061ed853adda6fa9f6f5
--- /dev/null
+++ b/lib/banzai/reference_parser/issue_parser.rb
@@ -0,0 +1,54 @@
+module Banzai
+  module ReferenceParser
+    class IssueParser < BaseParser
+      self.reference_type = :issue
+
+      def nodes_visible_to_user(user, nodes)
+        # It is not possible to check access rights for external issue trackers
+        return nodes if project && project.external_issue_tracker
+
+        issues = issues_for_nodes(nodes)
+
+        nodes.select do |node|
+          issue = issue_for_node(issues, node)
+
+          issue ? can?(user, :read_issue, issue) : false
+        end
+      end
+
+      def referenced_by(nodes)
+        issues = issues_for_nodes(nodes)
+
+        nodes.map { |node| issue_for_node(issues, node) }.uniq
+      end
+
+      def issues_for_nodes(nodes)
+        @issues_for_nodes ||= grouped_objects_for_nodes(
+          nodes,
+          Issue.all.includes(
+            :author,
+            :assignee,
+            {
+              # These associations are primarily used for checking permissions.
+              # Eager loading these ensures we don't end up running dozens of
+              # queries in this process.
+              project: [
+                { namespace: :owner },
+                { group: [:owners, :group_members] },
+                :invited_groups,
+                :project_members
+              ]
+            }
+          ),
+          self.class.data_attribute
+        )
+      end
+
+      private
+
+      def issue_for_node(issues, node)
+        issues[node.attr(self.class.data_attribute).to_i]
+      end
+    end
+  end
+end
diff --git a/lib/banzai/reference_parser/label_parser.rb b/lib/banzai/reference_parser/label_parser.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e5d1eb11d7f5d38c9256c124568cf07d5a5391ab
--- /dev/null
+++ b/lib/banzai/reference_parser/label_parser.rb
@@ -0,0 +1,11 @@
+module Banzai
+  module ReferenceParser
+    class LabelParser < BaseParser
+      self.reference_type = :label
+
+      def references_relation
+        Label
+      end
+    end
+  end
+end
diff --git a/lib/banzai/reference_parser/merge_request_parser.rb b/lib/banzai/reference_parser/merge_request_parser.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c9a9ca79c09c52c41b4654af68b06589162a9843
--- /dev/null
+++ b/lib/banzai/reference_parser/merge_request_parser.rb
@@ -0,0 +1,11 @@
+module Banzai
+  module ReferenceParser
+    class MergeRequestParser < BaseParser
+      self.reference_type = :merge_request
+
+      def references_relation
+        MergeRequest.includes(:author, :assignee, :target_project)
+      end
+    end
+  end
+end
diff --git a/lib/banzai/reference_parser/milestone_parser.rb b/lib/banzai/reference_parser/milestone_parser.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a000ac61e5c3f809974e9e2e4a9b5d66f45be0b0
--- /dev/null
+++ b/lib/banzai/reference_parser/milestone_parser.rb
@@ -0,0 +1,11 @@
+module Banzai
+  module ReferenceParser
+    class MilestoneParser < BaseParser
+      self.reference_type = :milestone
+
+      def references_relation
+        Milestone
+      end
+    end
+  end
+end
diff --git a/lib/banzai/reference_parser/snippet_parser.rb b/lib/banzai/reference_parser/snippet_parser.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fa71b3c952aa3fadf1b357c3a286464a44d1c3d0
--- /dev/null
+++ b/lib/banzai/reference_parser/snippet_parser.rb
@@ -0,0 +1,11 @@
+module Banzai
+  module ReferenceParser
+    class SnippetParser < BaseParser
+      self.reference_type = :snippet
+
+      def references_relation
+        Snippet
+      end
+    end
+  end
+end
diff --git a/lib/banzai/reference_parser/user_parser.rb b/lib/banzai/reference_parser/user_parser.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a12b0d1956010d9ad427a9399566e0313c3109a7
--- /dev/null
+++ b/lib/banzai/reference_parser/user_parser.rb
@@ -0,0 +1,92 @@
+module Banzai
+  module ReferenceParser
+    class UserParser < BaseParser
+      self.reference_type = :user
+
+      def referenced_by(nodes)
+        group_ids = []
+        user_ids = []
+        project_ids = []
+
+        nodes.each do |node|
+          if node.has_attribute?('data-group')
+            group_ids << node.attr('data-group').to_i
+          elsif node.has_attribute?(self.class.data_attribute)
+            user_ids << node.attr(self.class.data_attribute).to_i
+          elsif node.has_attribute?('data-project')
+            project_ids << node.attr('data-project').to_i
+          end
+        end
+
+        find_users_for_groups(group_ids) | find_users(user_ids) |
+          find_users_for_projects(project_ids)
+      end
+
+      def nodes_visible_to_user(user, nodes)
+        group_attr = 'data-group'
+        groups = lazy { grouped_objects_for_nodes(nodes, Group, group_attr) }
+        visible = []
+        remaining = []
+
+        nodes.each do |node|
+          if node.has_attribute?(group_attr)
+            node_group = groups[node.attr(group_attr).to_i]
+
+            if node_group &&
+              can?(user, :read_group, node_group)
+              visible << node
+            end
+          # Remaining nodes will be processed by the parent class'
+          # implementation of this method.
+          else
+            remaining << node
+          end
+        end
+
+        visible + super(current_user, remaining)
+      end
+
+      def nodes_user_can_reference(current_user, nodes)
+        project_attr = 'data-project'
+        author_attr = 'data-author'
+
+        projects = lazy { projects_for_nodes(nodes) }
+        users = lazy { grouped_objects_for_nodes(nodes, User, author_attr) }
+
+        nodes.select do |node|
+          project_id = node.attr(project_attr)
+          user_id = node.attr(author_attr)
+
+          if project && project_id && project.id == project_id.to_i
+            true
+          elsif project_id && user_id
+            project = projects[project_id.to_i]
+            user = users[user_id.to_i]
+
+            project && user ? project.team.member?(user) : false
+          else
+            true
+          end
+        end
+      end
+
+      def find_users(ids)
+        return [] if ids.empty?
+
+        User.where(id: ids).to_a
+      end
+
+      def find_users_for_groups(ids)
+        return [] if ids.empty?
+
+        User.joins(:group_members).where(members: { source_id: ids }).to_a
+      end
+
+      def find_users_for_projects(ids)
+        return [] if ids.empty?
+
+        Project.where(id: ids).flat_map { |p| p.team.members.to_a }
+      end
+    end
+  end
+end
diff --git a/lib/ci/ansi2html.rb b/lib/ci/ansi2html.rb
index 5fed43aaebd1e7e8309c72a092ca943d6373ae65..229050151d379e824a51fbc031378ec18154d758 100644
--- a/lib/ci/ansi2html.rb
+++ b/lib/ci/ansi2html.rb
@@ -90,7 +90,7 @@ module Ci
 
       def convert(raw, new_state)
         reset_state
-        restore_state(raw, new_state) if new_state
+        restore_state(raw, new_state) if new_state.present?
 
         start = @offset
         ansi = raw[@offset..-1]
@@ -98,13 +98,15 @@ module Ci
         open_new_tag
 
         s = StringScanner.new(ansi)
-        while(!s.eos?)
+        until s.eos?
           if s.scan(/\e([@-_])(.*?)([@-~])/)
             handle_sequence(s)
           elsif s.scan(/\e(([@-_])(.*?)?)?$/)
             break
           elsif s.scan(/</)
             @out << '&lt;'
+          elsif s.scan(/\n/)
+            @out << '<br>'
           else
             @out << s.scan(/./m)
           end
diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb
index 607359769d1537ff506cbf491de92ead1890a13c..9f270f7b38752ea9c786565d8458b06bb834b5bc 100644
--- a/lib/ci/api/builds.rb
+++ b/lib/ci/api/builds.rb
@@ -114,6 +114,7 @@ module Ci
         #   id (required) - The ID of a build
         #   token (required) - The build authorization token
         #   file (required) - Artifacts file
+        #   expire_in (optional) - Specify when artifacts should expire (ex. 7d)
         # Parameters (accelerated by GitLab Workhorse):
         #   file.path - path to locally stored body (generated by Workhorse)
         #   file.name - real filename as send in Content-Disposition
@@ -145,6 +146,7 @@ module Ci
 
           build.artifacts_file = artifacts
           build.artifacts_metadata = metadata
+          build.artifacts_expire_in = params['expire_in']
 
           if build.save
             present(build, with: Entities::BuildDetails)
diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb
index b25e0e573a8bd215402407aec01f7dcdf0013fbe..3f5bdaba3f5d0f757e23411ce83c72282fe54d52 100644
--- a/lib/ci/api/entities.rb
+++ b/lib/ci/api/entities.rb
@@ -20,7 +20,7 @@ module Ci
         expose :name, :token, :stage
         expose :project_id
         expose :project_name
-        expose :artifacts_file, using: ArtifactFile, if: lambda { |build, opts| build.artifacts? }
+        expose :artifacts_file, using: ArtifactFile, if: ->(build, _) { build.artifacts? }
       end
 
       class BuildDetails < Build
@@ -29,6 +29,7 @@ module Ci
         expose :before_sha
         expose :allow_git_fetch
         expose :token
+        expose :artifacts_expire_at, if: ->(build, _) { build.artifacts? }
 
         expose :options do |model|
           model.options
@@ -56,7 +57,7 @@ module Ci
 
       class TriggerRequest < Grape::Entity
         expose :id, :variables
-        expose :commit, using: Commit
+        expose :pipeline, using: Commit, as: :commit
       end
     end
   end
diff --git a/lib/ci/charts.rb b/lib/ci/charts.rb
index d53bdcbd0f22154ef56923ebc05b5b2cd070b732..5270108ef0fd0a73d4bf023c4e3166de98310506 100644
--- a/lib/ci/charts.rb
+++ b/lib/ci/charts.rb
@@ -60,11 +60,12 @@ module Ci
 
     class BuildTime < Chart
       def collect
-        commits = project.ci_commits.last(30)
+        commits = project.pipelines.last(30)
 
         commits.each do |commit|
           @labels << commit.short_sha
-          @build_times << (commit.duration / 60)
+          duration = commit.duration || 0
+          @build_times << (duration / 60)
         end
       end
     end
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index 504d3df9d349916d77437726eac22afc9eb0318d..68246497e90af470424c61dcfdbe59277b3aee2c 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -1,29 +1,32 @@
 module Ci
   class GitlabCiYamlProcessor
-    class ValidationError < StandardError;end
+    class ValidationError < StandardError; end
+
+    include Gitlab::Ci::Config::Node::ValidationHelpers
 
     DEFAULT_STAGES = %w(build test deploy)
     DEFAULT_STAGE = 'test'
     ALLOWED_YAML_KEYS = [:before_script, :after_script, :image, :services, :types, :stages, :variables, :cache]
     ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services,
                         :allow_failure, :type, :stage, :when, :artifacts, :cache,
-                        :dependencies, :before_script, :after_script, :variables]
+                        :dependencies, :before_script, :after_script, :variables,
+                        :environment]
+    ALLOWED_CACHE_KEYS = [:key, :untracked, :paths]
+    ALLOWED_ARTIFACTS_KEYS = [:name, :untracked, :paths, :when, :expire_in]
 
-    attr_reader :before_script, :after_script, :image, :services, :path, :cache
+    attr_reader :after_script, :image, :services, :path, :cache
 
     def initialize(config, path = nil)
-      @config = YAML.safe_load(config, [Symbol], [], true)
-      @path = path
-
-      unless @config.is_a? Hash
-        raise ValidationError, "YAML should be a hash"
-      end
+      @ci_config = Gitlab::Ci::Config.new(config)
+      @config = @ci_config.to_hash
 
-      @config = @config.deep_symbolize_keys
+      @path = path
 
       initial_parsing
 
       validate!
+    rescue Gitlab::Ci::Config::Loader::FormatError => e
+      raise ValidationError, e.message
     end
 
     def builds_for_stage_and_ref(stage, ref, tag = false, trigger_request = nil)
@@ -54,7 +57,6 @@ module Ci
     private
 
     def initial_parsing
-      @before_script = @config[:before_script] || []
       @after_script = @config[:after_script]
       @image = @config[:image]
       @services = @config[:services]
@@ -82,13 +84,14 @@ module Ci
       {
         stage_idx: stages.index(job[:stage]),
         stage: job[:stage],
-        commands: [job[:before_script] || @before_script, job[:script]].flatten.join("\n"),
+        commands: [job[:before_script] || [@ci_config.before_script], job[:script]].flatten.compact.join("\n"),
         tag_list: job[:tags] || [],
         name: name,
         only: job[:only],
         except: job[:except],
         allow_failure: job[:allow_failure] || false,
         when: job[:when] || 'on_success',
+        environment: job[:environment],
         options: {
           image: job[:image] || @image,
           services: job[:services] || @services,
@@ -101,6 +104,10 @@ module Ci
     end
 
     def validate!
+      unless @ci_config.valid?
+        raise ValidationError, @ci_config.errors.first
+      end
+
       validate_global!
 
       @jobs.each do |name, job|
@@ -111,10 +118,6 @@ module Ci
     end
 
     def validate_global!
-      unless validate_array_of_strings(@before_script)
-        raise ValidationError, "before_script should be an array of strings"
-      end
-
       unless @after_script.nil? || validate_array_of_strings(@after_script)
         raise ValidationError, "after_script should be an array of strings"
       end
@@ -139,6 +142,12 @@ module Ci
     end
 
     def validate_global_cache!
+      @cache.keys.each do |key|
+        unless ALLOWED_CACHE_KEYS.include? key
+          raise ValidationError, "#{name} cache unknown parameter #{key}"
+        end
+      end
+
       if @cache[:key] && !validate_string(@cache[:key])
         raise ValidationError, "cache:key parameter should be a string"
       end
@@ -204,9 +213,13 @@ module Ci
         raise ValidationError, "#{name} job: allow_failure parameter should be an boolean"
       end
 
-      if job[:when] && !job[:when].in?(%w(on_success on_failure always))
+      if job[:when] && !job[:when].in?(%w[on_success on_failure always])
         raise ValidationError, "#{name} job: when parameter should be on_success, on_failure or always"
       end
+
+      if job[:environment] && !validate_environment(job[:environment])
+        raise ValidationError, "#{name} job: environment parameter #{Gitlab::Regex.environment_name_regex_message}"
+      end
     end
 
     def validate_job_script!(name, job)
@@ -237,6 +250,12 @@ module Ci
     end
 
     def validate_job_cache!(name, job)
+      job[:cache].keys.each do |key|
+        unless ALLOWED_CACHE_KEYS.include? key
+          raise ValidationError, "#{name} job: cache unknown parameter #{key}"
+        end
+      end
+
       if job[:cache][:key] && !validate_string(job[:cache][:key])
         raise ValidationError, "#{name} job: cache:key parameter should be a string"
       end
@@ -251,6 +270,12 @@ module Ci
     end
 
     def validate_job_artifacts!(name, job)
+      job[:artifacts].keys.each do |key|
+        unless ALLOWED_ARTIFACTS_KEYS.include? key
+          raise ValidationError, "#{name} job: artifacts unknown parameter #{key}"
+        end
+      end
+
       if job[:artifacts][:name] && !validate_string(job[:artifacts][:name])
         raise ValidationError, "#{name} job: artifacts:name parameter should be a string"
       end
@@ -262,10 +287,18 @@ module Ci
       if job[:artifacts][:paths] && !validate_array_of_strings(job[:artifacts][:paths])
         raise ValidationError, "#{name} job: artifacts:paths parameter should be an array of strings"
       end
+
+      if job[:artifacts][:when] && !job[:artifacts][:when].in?(%w[on_success on_failure always])
+        raise ValidationError, "#{name} job: artifacts:when parameter should be on_success, on_failure or always"
+      end
+
+      if job[:artifacts][:expire_in] && !validate_duration(job[:artifacts][:expire_in])
+        raise ValidationError, "#{name} job: artifacts:expire_in parameter should be a duration"
+      end
     end
 
     def validate_job_dependencies!(name, job)
-      if !validate_array_of_strings(job[:dependencies])
+      unless validate_array_of_strings(job[:dependencies])
         raise ValidationError, "#{name} job: dependencies parameter should be an array of strings"
       end
 
@@ -280,22 +313,6 @@ module Ci
       end
     end
 
-    def validate_array_of_strings(values)
-      values.is_a?(Array) && values.all? { |value| validate_string(value) }
-    end
-
-    def validate_variables(variables)
-      variables.is_a?(Hash) && variables.all? { |key, value| validate_string(key) && validate_string(value) }
-    end
-
-    def validate_string(value)
-      value.is_a?(String) || value.is_a?(Symbol)
-    end
-
-    def validate_boolean(value)
-      value.in?([true, false])
-    end
-
     def process?(only_params, except_params, ref, tag, trigger_request)
       if only_params.present?
         return false unless matching?(only_params, ref, tag, trigger_request)
diff --git a/lib/container_registry/blob.rb b/lib/container_registry/blob.rb
index 4e20dc4f87534e4ddd13db860d6925399f5e2a4e..eb5a2596177e2a78c95d5e717ac3e8d2f49c466c 100644
--- a/lib/container_registry/blob.rb
+++ b/lib/container_registry/blob.rb
@@ -18,7 +18,7 @@ module ContainerRegistry
     end
 
     def digest
-      config['digest']
+      config['digest'] || config['blobSum']
     end
 
     def type
diff --git a/lib/container_registry/client.rb b/lib/container_registry/client.rb
index 4d726692f451569003dd2474988ecbd6935b07a9..e0b3f14d3849082de1ad81898ad484381549d1c1 100644
--- a/lib/container_registry/client.rb
+++ b/lib/container_registry/client.rb
@@ -47,7 +47,9 @@ module ContainerRegistry
       conn.request :json
       conn.headers['Accept'] = MANIFEST_VERSION
 
-      conn.response :json, content_type: /\bjson$/
+      conn.response :json, content_type: 'application/vnd.docker.distribution.manifest.v1+prettyjws'
+      conn.response :json, content_type: 'application/vnd.docker.distribution.manifest.v1+json'
+      conn.response :json, content_type: 'application/vnd.docker.distribution.manifest.v2+json'
 
       if options[:user] && options[:password]
         conn.request(:basic_auth, options[:user].to_s, options[:password].to_s)
diff --git a/lib/container_registry/tag.rb b/lib/container_registry/tag.rb
index 43f8d6dc8c24bddd15e0d24457df54823bbf8dd6..7a0929d774ed19cc867dc236e9c4f5cdf36b46e9 100644
--- a/lib/container_registry/tag.rb
+++ b/lib/container_registry/tag.rb
@@ -12,6 +12,14 @@ module ContainerRegistry
       manifest.present?
     end
 
+    def v1?
+      manifest && manifest['schemaVersion'] == 1
+    end
+
+    def v2?
+      manifest && manifest['schemaVersion'] == 2
+    end
+
     def manifest
       return @manifest if defined?(@manifest)
 
@@ -57,7 +65,9 @@ module ContainerRegistry
       return @layers if defined?(@layers)
       return unless manifest
 
-      @layers = manifest['layers'].map do |layer|
+      layers = manifest['layers'] || manifest['fsLayers']
+
+      @layers = layers.map do |layer|
         repository.blob(layer)
       end
     end
@@ -65,7 +75,7 @@ module ContainerRegistry
     def total_size
       return unless layers
 
-      layers.map(&:size).sum
+      layers.map(&:size).sum if v2?
     end
 
     def delete
diff --git a/lib/event_filter.rb b/lib/event_filter.rb
index f15b2cfd231090bc57b49bacd46849fd954120de..668d2fa41b3064f2fe16310a826e1e3cce268671 100644
--- a/lib/event_filter.rb
+++ b/lib/event_filter.rb
@@ -27,7 +27,7 @@ class EventFilter
     @params = if params
                 params.dup
               else
-                []#EventFilter.default_filter
+                [] # EventFilter.default_filter
               end
   end
 
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index 6520b82e2aa861b3de66836ced7ad870f2abe0eb..3d4db44e5f28e628afa53087575df1b462b22a06 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -1,22 +1,91 @@
 module Gitlab
-  class Auth
-    def find(login, password)
-      user = User.by_login(login)
-  
-      if Devise.omniauth_providers.include?(:kerberos)
-        kerberos_user = Gitlab::Kerberos::Authentication.login(login, password)
-        return kerberos_user if kerberos_user
+  module Auth
+    Result = Struct.new(:user, :type)
+
+    class << self
+      def find_for_git_client(login, password, project:, ip:)
+        raise "Must provide an IP for rate limiting" if ip.nil?
+
+        result = Result.new
+
+        if valid_ci_request?(login, password, project)
+          result.type = :ci
+        elsif result.user = find_with_user_password(login, password)
+          result.type = :gitlab_or_ldap
+        elsif result.user = oauth_access_token_check(login, password)
+          result.type = :oauth
+        end
+
+        rate_limit!(ip, success: !!result.user || (result.type == :ci), login: login)
+        result
+      end
+
+      def find_with_user_password(login, password)
+        user = User.by_login(login)
+
+        if Devise.omniauth_providers.include?(:kerberos)
+          kerberos_user = Gitlab::Kerberos::Authentication.login(login, password)
+          return kerberos_user if kerberos_user
+        end
+
+        # If no user is found, or it's an LDAP server, try LDAP.
+        #   LDAP users are only authenticated via LDAP
+        if user.nil? || user.ldap_user?
+          # Second chance - try LDAP authentication
+          return nil unless Gitlab::LDAP::Config.enabled?
+
+          Gitlab::LDAP::Authentication.login(login, password)
+        else
+          user if user.valid_password?(password)
+        end
       end
 
-      # If no user is found, or it's an LDAP server, try LDAP.
-      #   LDAP users are only authenticated via LDAP
-      if user.nil? || user.ldap_user?
-        # Second chance - try LDAP authentication
-        return nil unless Gitlab::LDAP::Config.enabled?
+      def rate_limit!(ip, success:, login:)
+        rate_limiter = Gitlab::Auth::IpRateLimiter.new(ip)
+        return unless rate_limiter.enabled?
+
+        if success
+          # Repeated login 'failures' are normal behavior for some Git clients so
+          # it is important to reset the ban counter once the client has proven
+          # they are not a 'bad guy'.
+          rate_limiter.reset!
+        else
+          # Register a login failure so that Rack::Attack can block the next
+          # request from this IP if needed.
+          rate_limiter.register_fail!
+
+          if rate_limiter.banned?
+            Rails.logger.info "IP #{ip} failed to login " \
+              "as #{login} but has been temporarily banned from Git auth"
+          end
+        end
+      end
+
+      private
+
+      def valid_ci_request?(login, password, project)
+        matched_login = /(?<service>^[a-zA-Z]*-ci)-token$/.match(login)
+
+        return false unless project && matched_login.present?
+
+        underscored_service = matched_login['service'].underscore
+
+        if underscored_service == 'gitlab_ci'
+          project && project.valid_build_token?(password)
+        elsif Service.available_services_names.include?(underscored_service)
+          # We treat underscored_service as a trusted input because it is included
+          # in the Service.available_services_names whitelist.
+          service = project.public_send("#{underscored_service}_service")
+
+          service && service.activated? && service.valid_token?(password)
+        end
+      end
 
-        Gitlab::LDAP::Authentication.login(login, password)
-      else
-        user if user.valid_password?(password)
+      def oauth_access_token_check(login, password)
+        if login == "oauth2" && password.present?
+          token = Doorkeeper::AccessToken.by_token(password)
+          token && token.accessible? && User.find_by(id: token.resource_owner_id)
+        end
       end
     end
   end
diff --git a/lib/gitlab/auth/ip_rate_limiter.rb b/lib/gitlab/auth/ip_rate_limiter.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1089bc9f89e43dd88be362a3a5bc5689f86e20b0
--- /dev/null
+++ b/lib/gitlab/auth/ip_rate_limiter.rb
@@ -0,0 +1,42 @@
+module Gitlab
+  module Auth
+    class IpRateLimiter
+      attr_reader :ip
+
+      def initialize(ip)
+        @ip = ip
+        @banned = false
+      end
+
+      def enabled?
+        config.enabled
+      end
+      
+      def reset!
+        Rack::Attack::Allow2Ban.reset(ip, config)
+      end
+      
+      def register_fail!
+        # Allow2Ban.filter will return false if this IP has not failed too often yet
+        @banned = Rack::Attack::Allow2Ban.filter(ip, config) do
+          # If we return false here, the failure for this IP is ignored by Allow2Ban
+          ip_can_be_banned?
+        end
+      end
+      
+      def banned?
+        @banned
+      end
+      
+      private
+      
+      def config
+        Gitlab.config.rack_attack.git_basic_auth
+      end
+      
+      def ip_can_be_banned?
+        config.ip_whitelist.exclude?(ip)
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/authority_analyzer.rb b/lib/gitlab/authority_analyzer.rb
index a6fea82e754b118565c54b4ece56dde56af2d869..01d43b72015057d75ed3cad892e8ea5842b0f43e 100644
--- a/lib/gitlab/authority_analyzer.rb
+++ b/lib/gitlab/authority_analyzer.rb
@@ -2,9 +2,8 @@ module Gitlab
   class AuthorityAnalyzer
     COMMITS_TO_CONSIDER = 5
 
-    def initialize(merge_request, current_user)
+    def initialize(merge_request)
       @merge_request = merge_request
-      @current_user = current_user
       @users = Hash.new(0)
     end
 
@@ -12,7 +11,7 @@ module Gitlab
       involved_users
 
       # Picks most active users from hash like: {user1: 2, user2: 6}
-      @users.sort_by { |user, count| count }.map(&:first).take(number_of_approvers)
+      @users.sort_by { |user, count| -count }.map(&:first).take(number_of_approvers)
     end
 
     private
@@ -22,7 +21,9 @@ module Gitlab
 
       list_of_involved_files.each do |path|
         @repo.commits(@merge_request.target_branch, path: path, limit: COMMITS_TO_CONSIDER).each do |commit|
-          @users[commit.author] += 1 if commit.author
+          if commit.author && commit.author != @merge_request.author
+            @users[commit.author] += 1
+          end
         end
       end
     end
diff --git a/lib/gitlab/award_emoji.rb b/lib/gitlab/award_emoji.rb
new file mode 100644
index 0000000000000000000000000000000000000000..51b1df9ecbd86a878771c7a664f4ea11d91217cd
--- /dev/null
+++ b/lib/gitlab/award_emoji.rb
@@ -0,0 +1,84 @@
+module Gitlab
+  class AwardEmoji
+    CATEGORIES = {
+      other: "Other",
+      objects: "Objects",
+      places: "Places",
+      travel_places: "Travel",
+      emoticons: "Emoticons",
+      objects_symbols: "Symbols",
+      nature: "Nature",
+      celebration: "Celebration",
+      people: "People",
+      activity: "Activity",
+      flags: "Flags",
+      food_drink: "Food"
+    }.with_indifferent_access
+
+    CATEGORY_ALIASES = {
+      symbols: "objects_symbols",
+      foods: "food_drink",
+      travel: "travel_places"
+    }.with_indifferent_access
+
+    def self.normalize_emoji_name(name)
+      aliases[name] || name
+    end
+
+    def self.emoji_by_category
+      unless @emoji_by_category
+        @emoji_by_category = Hash.new { |h, key| h[key] = [] }
+
+        emojis.each do |emoji_name, data|
+          data["name"] = emoji_name
+
+          # Skip Fitzpatrick(tone) modifiers
+          next if data["category"] == "modifier"
+
+          category = CATEGORY_ALIASES[data["category"]] || data["category"]
+
+          @emoji_by_category[category] << data
+        end
+
+        @emoji_by_category = @emoji_by_category.sort.to_h
+      end
+
+      @emoji_by_category
+    end
+
+    def self.emojis
+      @emojis ||=
+        begin
+          json_path = File.join(Rails.root, 'fixtures', 'emojis', 'index.json' )
+          JSON.parse(File.read(json_path))
+        end
+    end
+
+    def self.aliases
+      @aliases ||=
+        begin
+         json_path = File.join(Rails.root, 'fixtures', 'emojis', 'aliases.json' )
+         JSON.parse(File.read(json_path))
+       end
+    end
+
+    # Returns an Array of Emoji names and their asset URLs.
+    def self.urls
+      @urls ||= begin
+                  path = File.join(Rails.root, 'fixtures', 'emojis', 'digests.json')
+                  prefix = Gitlab::Application.config.assets.prefix
+                  digest = Gitlab::Application.config.assets.digest
+
+                  JSON.parse(File.read(path)).map do |hash|
+                    if digest
+                      fname = "#{hash['unicode']}-#{hash['digest']}"
+                    else
+                      fname = hash['unicode']
+                    end
+
+                    { name: hash['name'], path: "#{prefix}/#{fname}.png" }
+                  end
+                end
+    end
+  end
+end
diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb
index 9754754492534ed3277fae85f5de669fb5cd3522..e474c53898d26c5fe2e5e5da6697cf5fead46697 100644
--- a/lib/gitlab/backend/grack_auth.rb
+++ b/lib/gitlab/backend/grack_auth.rb
@@ -1,5 +1,3 @@
-require_relative 'shell_env'
-
 module Grack
   class AuthSpawner
     def self.call(env)
@@ -41,10 +39,7 @@ module Grack
       lfs_response = Gitlab::Lfs::Router.new(project, @user, @request).try_call
       return lfs_response unless lfs_response.nil?
 
-      if project && authorized_request?
-        # Tell gitlab-workhorse the request is OK, and what the GL_ID is
-        render_grack_auth_ok
-      elsif @user.nil? && !@ci
+      if @user.nil? && !@ci
         unauthorized
       else
         render_not_found
@@ -119,11 +114,6 @@ module Grack
 
         @user = authenticate_user(login, password)
       end
-
-      if @user
-        Gitlab::ShellEnv.set_env(@user)
-        @env['REMOTE_USER'] = @auth.username
-      end
     end
 
     def ci_request?(login, password)
@@ -153,7 +143,7 @@ module Grack
     end
 
     def authenticate_user(login, password)
-      user = Gitlab::Auth.new.find(login, password)
+      user = Gitlab::Auth.find_with_user_password(login, password)
 
       unless user
         user = oauth_access_token_check(login, password)
@@ -196,36 +186,6 @@ module Grack
       user
     end
 
-    def authorized_request?
-      return true if @ci
-
-      case git_cmd
-      when *Gitlab::GitAccess::DOWNLOAD_COMMANDS
-        if !Gitlab.config.gitlab_shell.upload_pack
-          false
-        elsif user
-          Gitlab::GitAccess.new(user, project).download_access_check.allowed?
-        elsif project.public?
-          # Allow clone/fetch for public projects
-          true
-        else
-          false
-        end
-      when *Gitlab::GitAccess::PUSH_COMMANDS
-        if !Gitlab.config.gitlab_shell.receive_pack
-          false
-        elsif user
-          # Skip user authorization on upload request.
-          # It will be done by the pre-receive hook in the repository.
-          true
-        else
-          false
-        end
-      else
-        false
-      end
-    end
-
     def git_cmd
       if @request.get?
         @request.params['service']
@@ -252,24 +212,6 @@ module Grack
       end
     end
 
-    def render_grack_auth_ok
-      repo_path =
-        if @request.path_info =~ /^([\w\.\/-]+)\.wiki\.git/
-          ProjectWiki.new(project).repository.path_to_repo
-        else
-          project.repository.path_to_repo
-        end
-
-      [
-        200,
-        { "Content-Type" => "application/json" },
-        [JSON.dump({
-          'GL_ID' => Gitlab::ShellEnv.gl_id(@user),
-          'RepoPath' => repo_path,
-        })]
-      ]
-    end
-
     def render_not_found
       [404, { "Content-Type" => "text/plain" }, ["Not Found"]]
     end
diff --git a/lib/gitlab/backend/shell_env.rb b/lib/gitlab/backend/shell_env.rb
deleted file mode 100644
index 9f5adee594a6857201585bd3b6d1174c3f090ee7..0000000000000000000000000000000000000000
--- a/lib/gitlab/backend/shell_env.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-module Gitlab
-  # This module provide 2 methods
-  # to set specific ENV variables for GitLab Shell
-  module ShellEnv
-    extend self
-
-    def set_env(user)
-      # Set GL_ID env variable
-      if user
-        ENV['GL_ID'] = gl_id(user)
-      end
-    end
-
-    def reset_env
-      # Reset GL_ID env variable
-      ENV['GL_ID'] = nil
-    end
-
-    def gl_id(user)
-      if user.present?
-        "user-#{user.id}"
-      else
-        # This empty string is used in the render_grack_auth_ok method
-        ""
-      end
-    end
-  end
-end
diff --git a/lib/gitlab/bitbucket_import/client.rb b/lib/gitlab/bitbucket_import/client.rb
index 9b83292ef33adceaa56417156d3252581d977254..8d1ad62fae0b088e51944a88ea45adc86a5c8844 100644
--- a/lib/gitlab/bitbucket_import/client.rb
+++ b/lib/gitlab/bitbucket_import/client.rb
@@ -121,7 +121,7 @@ module Gitlab
 
       def get(url)
         response = api.get(url)
-        raise Unauthorized if (400..499).include?(response.code.to_i)
+        raise Unauthorized if (400..499).cover?(response.code.to_i)
 
         response
       end
diff --git a/lib/gitlab/build_data_builder.rb b/lib/gitlab/build_data_builder.rb
index 34e949130da1a0627c51acc5111447f85024747c..9f45aefda0f6937eaa1d8e52689e429b4a61e849 100644
--- a/lib/gitlab/build_data_builder.rb
+++ b/lib/gitlab/build_data_builder.rb
@@ -3,7 +3,7 @@ module Gitlab
     class << self
       def build(build)
         project = build.project
-        commit = build.commit
+        commit = build.pipeline
         user = build.user
 
         data = {
diff --git a/lib/gitlab/ci/build/artifacts/metadata.rb b/lib/gitlab/ci/build/artifacts/metadata.rb
index f2020c82d40543d1baf443a66fa0eb44c63dade6..cd2e83b4c27e96e7b41ad3777a4ff97832a596e8 100644
--- a/lib/gitlab/ci/build/artifacts/metadata.rb
+++ b/lib/gitlab/ci/build/artifacts/metadata.rb
@@ -56,7 +56,7 @@ module Gitlab
             child_pattern = '[^/]*/?$' unless @opts[:recursive]
             match_pattern = /^#{Regexp.escape(@path)}#{child_pattern}/
 
-            until gz.eof? do
+            until gz.eof?
               begin
                 path = read_string(gz).force_encoding('UTF-8')
                 meta = read_string(gz).force_encoding('UTF-8')
diff --git a/lib/gitlab/ci/config.rb b/lib/gitlab/ci/config.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b48d3592f1699c6473700703b299b3ccd3e6968c
--- /dev/null
+++ b/lib/gitlab/ci/config.rb
@@ -0,0 +1,26 @@
+module Gitlab
+  module Ci
+    ##
+    # Base GitLab CI Configuration facade
+    #
+    class Config
+      delegate :valid?, :errors, to: :@global
+
+      ##
+      # Temporary delegations that should be removed after refactoring
+      #
+      delegate :before_script, to: :@global
+
+      def initialize(config)
+        @config = Loader.new(config).load!
+
+        @global = Node::Global.new(@config)
+        @global.process!
+      end
+
+      def to_hash
+        @config
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/ci/config/loader.rb b/lib/gitlab/ci/config/loader.rb
new file mode 100644
index 0000000000000000000000000000000000000000..dbf6eb0edbe6625f60db0111822cf9b482919e05
--- /dev/null
+++ b/lib/gitlab/ci/config/loader.rb
@@ -0,0 +1,25 @@
+module Gitlab
+  module Ci
+    class Config
+      class Loader
+        class FormatError < StandardError; end
+
+        def initialize(config)
+          @config = YAML.safe_load(config, [Symbol], [], true)
+        end
+
+        def valid?
+          @config.is_a?(Hash)
+        end
+
+        def load!
+          unless valid?
+            raise FormatError, 'Invalid configuration format'
+          end
+
+          @config.deep_symbolize_keys
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/ci/config/node/configurable.rb b/lib/gitlab/ci/config/node/configurable.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d60f87f3f94d5e127932c410600aa2183c89a3bc
--- /dev/null
+++ b/lib/gitlab/ci/config/node/configurable.rb
@@ -0,0 +1,61 @@
+module Gitlab
+  module Ci
+    class Config
+      module Node
+        ##
+        # This mixin is responsible for adding DSL, which purpose is to
+        # simplifly process of adding child nodes.
+        #
+        # This can be used only if parent node is a configuration entry that
+        # holds a hash as a configuration value, for example:
+        #
+        # job:
+        #   script: ...
+        #   artifacts: ...
+        #
+        module Configurable
+          extend ActiveSupport::Concern
+
+          def allowed_nodes
+            self.class.allowed_nodes || {}
+          end
+
+          private
+
+          def prevalidate!
+            unless @value.is_a?(Hash)
+              @errors << 'should be a configuration entry with hash value'
+            end
+          end
+
+          def create_node(key, factory)
+            factory.with(value: @value[key])
+            factory.nullify! unless @value.has_key?(key)
+            factory.create!
+          end
+
+          class_methods do
+            def allowed_nodes
+              Hash[@allowed_nodes.map { |key, factory| [key, factory.dup] }]
+            end
+
+            private
+
+            def allow_node(symbol, entry_class, metadata)
+              factory = Node::Factory.new(entry_class)
+                .with(description: metadata[:description])
+
+              define_method(symbol) do
+                raise Entry::InvalidError unless valid?
+
+                @nodes[symbol].try(:value)
+              end
+
+              (@allowed_nodes ||= {}).merge!(symbol => factory)
+            end
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/ci/config/node/entry.rb b/lib/gitlab/ci/config/node/entry.rb
new file mode 100644
index 0000000000000000000000000000000000000000..52758a962f3c79989cd36582be5dc86114049853
--- /dev/null
+++ b/lib/gitlab/ci/config/node/entry.rb
@@ -0,0 +1,77 @@
+module Gitlab
+  module Ci
+    class Config
+      module Node
+        ##
+        # Base abstract class for each configuration entry node.
+        #
+        class Entry
+          class InvalidError < StandardError; end
+
+          attr_accessor :description
+
+          def initialize(value)
+            @value = value
+            @nodes = {}
+            @errors = []
+
+            prevalidate!
+          end
+
+          def process!
+            return if leaf?
+            return unless valid?
+
+            compose!
+
+            nodes.each(&:process!)
+            nodes.each(&:validate!)
+          end
+
+          def nodes
+            @nodes.values
+          end
+
+          def valid?
+            errors.none?
+          end
+
+          def leaf?
+            allowed_nodes.none?
+          end
+
+          def errors
+            @errors + nodes.map(&:errors).flatten
+          end
+
+          def allowed_nodes
+            {}
+          end
+
+          def validate!
+            raise NotImplementedError
+          end
+
+          def value
+            raise NotImplementedError
+          end
+
+          private
+
+          def prevalidate!
+          end
+
+          def compose!
+            allowed_nodes.each do |key, essence|
+              @nodes[key] = create_node(key, essence)
+            end
+          end
+
+          def create_node(key, essence)
+            raise NotImplementedError
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/ci/config/node/factory.rb b/lib/gitlab/ci/config/node/factory.rb
new file mode 100644
index 0000000000000000000000000000000000000000..787ca006f5abb8291ee4a034a56348db121aa06f
--- /dev/null
+++ b/lib/gitlab/ci/config/node/factory.rb
@@ -0,0 +1,39 @@
+module Gitlab
+  module Ci
+    class Config
+      module Node
+        ##
+        # Factory class responsible for fabricating node entry objects.
+        #
+        # It uses Fluent Interface pattern to set all necessary attributes.
+        #
+        class Factory
+          class InvalidFactory < StandardError; end
+
+          def initialize(entry_class)
+            @entry_class = entry_class
+            @attributes = {}
+          end
+
+          def with(attributes)
+            @attributes.merge!(attributes)
+            self
+          end
+
+          def nullify!
+            @entry_class = Node::Null
+            self
+          end
+
+          def create!
+            raise InvalidFactory unless @attributes.has_key?(:value)
+
+            @entry_class.new(@attributes[:value]).tap do |entry|
+              entry.description = @attributes[:description]
+            end
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/ci/config/node/global.rb b/lib/gitlab/ci/config/node/global.rb
new file mode 100644
index 0000000000000000000000000000000000000000..044603423d51d5489b6a3e644b300b7c65683d54
--- /dev/null
+++ b/lib/gitlab/ci/config/node/global.rb
@@ -0,0 +1,18 @@
+module Gitlab
+  module Ci
+    class Config
+      module Node
+        ##
+        # This class represents a global entry - root node for entire
+        # GitLab CI Configuration file.
+        #
+        class Global < Entry
+          include Configurable
+
+          allow_node :before_script, Script,
+            description: 'Script that will be executed before each job.'
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/ci/config/node/null.rb b/lib/gitlab/ci/config/node/null.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4f590f6bec80425e5f22b82d362730571dfa0124
--- /dev/null
+++ b/lib/gitlab/ci/config/node/null.rb
@@ -0,0 +1,27 @@
+module Gitlab
+  module Ci
+    class Config
+      module Node
+        ##
+        # This class represents a configuration entry that is not being used
+        # in configuration file.
+        #
+        # This implements Null Object pattern.
+        #
+        class Null < Entry
+          def value
+            nil
+          end
+
+          def validate!
+            nil
+          end
+
+          def method_missing(*)
+            nil
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/ci/config/node/script.rb b/lib/gitlab/ci/config/node/script.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5072bf0db7d56ef4016629cfb229ffa8a6b0fb5c
--- /dev/null
+++ b/lib/gitlab/ci/config/node/script.rb
@@ -0,0 +1,29 @@
+module Gitlab
+  module Ci
+    class Config
+      module Node
+        ##
+        # Entry that represents a script.
+        #
+        # Each element in the value array is a command that will be executed
+        # by GitLab Runner. Currently we concatenate these commands with
+        # new line character as a separator, what is compatible with
+        # implementation in Runner.
+        #
+        class Script < Entry
+          include ValidationHelpers
+
+          def value
+            @value.join("\n")
+          end
+
+          def validate!
+            unless validate_array_of_strings(@value)
+              @errors << 'before_script should be an array of strings'
+            end
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/ci/config/node/validation_helpers.rb b/lib/gitlab/ci/config/node/validation_helpers.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3900fc89391f60b56a0f266a7d45cd5625e883d1
--- /dev/null
+++ b/lib/gitlab/ci/config/node/validation_helpers.rb
@@ -0,0 +1,38 @@
+module Gitlab
+  module Ci
+    class Config
+      module Node
+        module ValidationHelpers
+          private
+
+          def validate_duration(value)
+            value.is_a?(String) && ChronicDuration.parse(value)
+          rescue ChronicDuration::DurationParseError
+            false
+          end
+
+          def validate_array_of_strings(values)
+            values.is_a?(Array) && values.all? { |value| validate_string(value) }
+          end
+
+          def validate_variables(variables)
+            variables.is_a?(Hash) &&
+              variables.all? { |key, value| validate_string(key) && validate_string(value) }
+          end
+
+          def validate_string(value)
+            value.is_a?(String) || value.is_a?(Symbol)
+          end
+
+          def validate_environment(value)
+            value.is_a?(String) && value =~ Gitlab::Regex.environment_name_regex
+          end
+
+          def validate_boolean(value)
+            value.in?([true, false])
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/contributions_calendar.rb b/lib/gitlab/contributions_calendar.rb
index 85583dce9eeb75cfa66860ce966fca2c998e71af..9dc2602867e07da8731da1eedb2cb31796a03596 100644
--- a/lib/gitlab/contributions_calendar.rb
+++ b/lib/gitlab/contributions_calendar.rb
@@ -19,7 +19,7 @@ module Gitlab
         select('date(created_at) as date, count(id) as total_amount').
         map(&:attributes)
 
-      dates = (1.year.ago.to_date..(Date.today + 1.day)).to_a
+      dates = (1.year.ago.to_date..Date.today).to_a
 
       dates.each do |date|
         date_id = date.to_time.to_i.to_s
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index f44d1b3a44ec91dc1c3c351ec28a17ff63997bb4..c215c7768b0aca84a5e4f60e48ab615b4e9e32bc 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -1,18 +1,22 @@
 module Gitlab
   module CurrentSettings
     def current_application_settings
-      key = :current_application_settings
-
-      RequestStore.store[key] ||= begin
-        settings = nil
+      if RequestStore.active?
+        RequestStore.fetch(:current_application_settings) { ensure_application_settings! }
+      else
+        ensure_application_settings!
+      end
+    end
 
-        if connect_to_db?
-          settings = ::ApplicationSetting.current
-          settings ||= ::ApplicationSetting.create_from_defaults unless ActiveRecord::Migrator.needs_migration?
-        end
+    def ensure_application_settings!
+      settings = ::ApplicationSetting.cached
 
-        settings || fake_application_settings
+      if !settings && connect_to_db?
+        settings = ::ApplicationSetting.current
+        settings ||= ::ApplicationSetting.create_from_defaults unless ActiveRecord::Migrator.needs_migration?
       end
+
+      settings || fake_application_settings
     end
 
     def fake_application_settings
@@ -22,7 +26,10 @@ module Gitlab
         signup_enabled: Settings.gitlab['signup_enabled'],
         signin_enabled: Settings.gitlab['signin_enabled'],
         gravatar_enabled: Settings.gravatar['enabled'],
-        sign_in_text: Settings.extra['sign_in_text'],
+        sign_in_text: nil,
+        after_sign_up_text: nil,
+        help_page_text: nil,
+        shared_runners_text: nil,
         restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
         max_attachment_size: Settings.gitlab['max_attachment_size'],
         session_expire_delay: Settings.gitlab['session_expire_delay'],
@@ -36,6 +43,11 @@ module Gitlab
         two_factor_grace_period: 48,
         akismet_enabled: false,
         repository_checks_enabled: true,
+        container_registry_token_expire_delay: 5,
+        elasticsearch_search: false,
+        elasticsearch_indexing: false,
+        elasticsearch_host: ENV['ELASTIC_HOST'] || 'localhost',
+        elasticsearch_port: ENV['ELASTIC_PORT'] || '9200'
       )
     end
 
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 42bec913a45ce7b4ff1cf7edd4b815844d1bcbbf..d76ecb54017d17317c2f93987c265d5b94b9f70f 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -16,6 +16,24 @@ module Gitlab
       database_version.match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1]
     end
 
+    def self.nulls_last_order(field, direction = 'ASC')
+      order = "#{field} #{direction}"
+
+      if Gitlab::Database.postgresql?
+        order << ' NULLS LAST'
+      else
+        # `field IS NULL` will be `0` for non-NULL columns and `1` for NULL
+        # columns. In the (default) ascending order, `0` comes first.
+        order.prepend("#{field} IS NULL, ") if direction == 'ASC'
+      end
+
+      order
+    end
+
+    def self.random
+      Gitlab::Database.postgresql? ? "RANDOM()" : "RAND()"
+    end
+
     def true_value
       if Gitlab::Database.postgresql?
         "'t'"
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index fd14234c5581b20284bc4f35d2c2333d2d28ad02..dd3ff0ab18b608d3c7c20a4cfddf3f7400c8d84d 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -11,7 +11,7 @@ module Gitlab
       #     add_concurrent_index :users, :some_column
       #
       # See Rails' `add_index` for more info on the available arguments.
-      def add_concurrent_index(*args)
+      def add_concurrent_index(table_name, column_name, options = {})
         if transaction_open?
           raise 'add_concurrent_index can not be run inside a transaction, ' \
             'you can disable transactions by calling disable_ddl_transaction! ' \
@@ -19,10 +19,10 @@ module Gitlab
         end
 
         if Database.postgresql?
-          args << { algorithm: :concurrently }
+          options = options.merge({ algorithm: :concurrently })
         end
 
-        add_index(*args)
+        add_index(table_name, column_name, options)
       end
 
       # Updates the value of a column in batches.
@@ -31,8 +31,6 @@ module Gitlab
       # Any data inserted while running this method (or after it has finished
       # running) is _not_ updated automatically.
       #
-      # This method _only_ updates rows where the column's value is set to NULL.
-      #
       # table - The name of the table.
       # column - The name of the column to update.
       # value - The value for the column.
@@ -55,10 +53,10 @@ module Gitlab
           first['count'].
           to_i
 
-        # Update in batches of 5%
+        # Update in batches of 5% until we run out of any rows to update.
         batch_size = ((total / 100.0) * 5.0).ceil
 
-        while processed < total
+        loop do
           start_row = exec_query(%Q{
             SELECT id
             FROM #{quoted_table}
@@ -66,6 +64,9 @@ module Gitlab
             LIMIT 1 OFFSET #{processed}
           }).to_hash.first
 
+          # There are no more rows to process
+          break unless start_row
+
           stop_row = exec_query(%Q{
             SELECT id
             FROM #{quoted_table}
@@ -126,6 +127,8 @@ module Gitlab
         begin
           transaction do
             update_column_in_batches(table, column, default)
+
+            change_column_null(table, column, false) unless allow_null
           end
         # We want to rescue _all_ exceptions here, even those that don't inherit
         # from StandardError.
@@ -134,8 +137,6 @@ module Gitlab
 
           raise error
         end
-
-        change_column_null(table, column, false) unless allow_null
       end
     end
   end
diff --git a/lib/gitlab/diff/parser.rb b/lib/gitlab/diff/parser.rb
index 6fe7faa547a0909030ff4adec3b67f85b9dc4aaa..522dd2b942897de3cea13eda428b41403d5a33ae 100644
--- a/lib/gitlab/diff/parser.rb
+++ b/lib/gitlab/diff/parser.rb
@@ -17,16 +17,16 @@ module Gitlab
         Enumerator.new do |yielder|
           @lines.each do |line|
             next if filename?(line)
-  
+
             full_line = line.delete("\n")
-  
+
             if line.match(/^@@ -/)
               type = "match"
-  
+
               line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0
               line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0
-  
-              next if line_old <= 1 && line_new <= 1 #top of file
+
+              next if line_old <= 1 && line_new <= 1 # top of file
               yielder << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new)
               line_obj_index += 1
               next
@@ -39,8 +39,8 @@ module Gitlab
               yielder << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new)
               line_obj_index += 1
             end
-  
-  
+
+
             case line[0]
             when "+"
               line_new += 1
diff --git a/lib/gitlab/elastic/indexer.rb b/lib/gitlab/elastic/indexer.rb
index 83f76409839751973ba59daeded14299784f51c5..278c9225886d6233384f1a3baf2b5ee1aab99ee6 100644
--- a/lib/gitlab/elastic/indexer.rb
+++ b/lib/gitlab/elastic/indexer.rb
@@ -1,12 +1,14 @@
 module Gitlab
   module Elastic
     class Indexer
+      include Gitlab::CurrentSettings
+
       Error = Class.new(StandardError)
 
       def initialize
         connection_info = {
-          host: Gitlab.config.elasticsearch.host,
-          port: Gitlab.config.elasticsearch.port
+          host: current_application_settings.elasticsearch_host,
+          port: current_application_settings.elasticsearch_port
         }.to_json
 
         # We accept any form of settings, including string and array
diff --git a/lib/gitlab/elastic/project_search_results.rb b/lib/gitlab/elastic/project_search_results.rb
index 78e53af685392f2c132944e8ac4306776a7bcf73..578ec74ceb3f6e6057d786a032e0666f3ae94d3b 100644
--- a/lib/gitlab/elastic/project_search_results.rb
+++ b/lib/gitlab/elastic/project_search_results.rb
@@ -86,7 +86,8 @@ module Gitlab
 
       def notes
         opt = {
-          project_ids: limit_project_ids
+          project_ids: limit_project_ids,
+          current_user: @current_user
         }
 
         Note.elastic_search(query, options: opt)
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index 072491db47d6e1c5989690f225109a437808f671..105ee4714fc746d21fa44660945355d1ca67c875 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -1,5 +1,7 @@
 module Gitlab
   class GitAccess
+    include PathLocksHelper
+
     DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive }
     PUSH_COMMANDS = %w{ git-receive-pack }
     GIT_ANNEX_COMMANDS = %w{ git-annex-shell }
@@ -97,7 +99,6 @@ module Gitlab
     end
 
     def push_access_check(changes)
-
       if Gitlab::Geo.secondary?
         return build_status_object(false, "You can't push code on a secondary GitLab Geo node.")
       end
@@ -186,13 +187,47 @@ module Gitlab
 
       # Return build_status_object(true) if all git hook checks passed successfully
       # or build_status_object(false) if any hook fails
-      git_hook_check(user, project, ref, oldrev, newrev)
+      result = git_hook_check(user, project, ref, oldrev, newrev)
+
+      if result.status && license_allows_file_locks?
+        result = path_locks_check(user, project, ref, oldrev, newrev)
+      end
+
+      result
     end
 
     def forced_push?(oldrev, newrev)
       Gitlab::ForcePushCheck.force_push?(project, oldrev, newrev)
     end
 
+    def path_locks_check(user, project, ref, oldrev, newrev)
+      unless project.path_locks.any? && newrev && oldrev
+        return build_status_object(true)
+      end
+
+      # locks protect default branch only
+      if project.default_branch != branch_name(ref)
+        return build_status_object(true)
+      end
+
+      commits(newrev, oldrev, project).each do |commit|
+        next if commit_from_annex_sync?(commit.safe_message)
+
+        commit.diffs.each do |diff|
+          path = diff.new_path || diff.old_path
+
+          lock_info = project.path_lock_info(path)
+
+          if lock_info && lock_info.user != user
+            return build_status_object(false, "The path '#{lock_info.path}' is locked by #{lock_info.user.name}")
+          end
+        end
+      end
+
+
+      build_status_object(true)
+    end
+
     def git_hook_check(user, project, ref, oldrev, newrev)
       unless project.git_hook && newrev && oldrev
         return build_status_object(true)
@@ -385,7 +420,8 @@ module Gitlab
     end
 
     def old_commit?(commit)
-      commit.refs(project.repository).any?
+      # We skip refs/tmp ref because we use it for Web UI commiting
+      commit.refs(project.repository).reject { |ref| ref.name.start_with?('refs/tmp') }.any?
     end
   end
 end
diff --git a/lib/gitlab/github_import/base_formatter.rb b/lib/gitlab/github_import/base_formatter.rb
index 202263c6742643dc6ccf56b1367d95573994e8b0..72992baffd40df8abbbce6dc8512269d2e264772 100644
--- a/lib/gitlab/github_import/base_formatter.rb
+++ b/lib/gitlab/github_import/base_formatter.rb
@@ -9,6 +9,10 @@ module Gitlab
         @formatter = Gitlab::ImportFormatter.new
       end
 
+      def create!
+        self.klass.create!(self.attributes)
+      end
+
       private
 
       def gl_user_id(github_id)
diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb
index 67988ea34604892d3169e70215e4fc6ea2fa3219..d325eca6d99cd4ab6a6d0a967a8a0b3a1b305803 100644
--- a/lib/gitlab/github_import/client.rb
+++ b/lib/gitlab/github_import/client.rb
@@ -1,6 +1,9 @@
 module Gitlab
   module GithubImport
     class Client
+      GITHUB_SAFE_REMAINING_REQUESTS = 100
+      GITHUB_SAFE_SLEEP_TIME = 500
+
       attr_reader :client, :api
 
       def initialize(access_token)
@@ -11,7 +14,7 @@ module Gitlab
         )
 
         if access_token
-          ::Octokit.auto_paginate = true
+          ::Octokit.auto_paginate = false
 
           @api = ::Octokit::Client.new(
             access_token: access_token,
@@ -36,7 +39,7 @@ module Gitlab
 
       def method_missing(method, *args, &block)
         if api.respond_to?(method)
-          api.send(method, *args, &block)
+          request { api.send(method, *args, &block) }
         else
           super(method, *args, &block)
         end
@@ -55,6 +58,34 @@ module Gitlab
       def github_options
         config["args"]["client_options"].deep_symbolize_keys
       end
+
+      def rate_limit
+        api.rate_limit!
+      end
+
+      def rate_limit_exceed?
+        rate_limit.remaining <= GITHUB_SAFE_REMAINING_REQUESTS
+      end
+
+      def rate_limit_sleep_time
+        rate_limit.resets_in + GITHUB_SAFE_SLEEP_TIME
+      end
+
+      def request
+        sleep rate_limit_sleep_time if rate_limit_exceed?
+
+        data = yield
+
+        last_response = api.last_response
+
+        while last_response.rels[:next]
+          sleep rate_limit_sleep_time if rate_limit_exceed?
+          last_response = last_response.rels[:next].get
+          data.concat(last_response.data) if last_response.data.is_a?(Array)
+        end
+
+        data
+      end
     end
   end
 end
diff --git a/lib/gitlab/github_import/comment_formatter.rb b/lib/gitlab/github_import/comment_formatter.rb
index 7d679eaec6aebb436d28cde191f5607ec775136a..2c1b94ef2cd781c1288d48917169994c1a82dfd6 100644
--- a/lib/gitlab/github_import/comment_formatter.rb
+++ b/lib/gitlab/github_import/comment_formatter.rb
@@ -8,6 +8,7 @@ module Gitlab
           commit_id: raw_data.commit_id,
           line_code: line_code,
           author_id: author_id,
+          type: type,
           created_at: raw_data.created_at,
           updated_at: raw_data.updated_at
         }
@@ -53,6 +54,10 @@ module Gitlab
       def note
         formatter.author_line(author) + body
       end
+
+      def type
+        'LegacyDiffNote' if on_diff?
+      end
     end
   end
 end
diff --git a/lib/gitlab/github_import/hook_formatter.rb b/lib/gitlab/github_import/hook_formatter.rb
new file mode 100644
index 0000000000000000000000000000000000000000..db1fabaa18af50ed0f1d241ac8395361e7d21cc5
--- /dev/null
+++ b/lib/gitlab/github_import/hook_formatter.rb
@@ -0,0 +1,23 @@
+module Gitlab
+  module GithubImport
+    class HookFormatter
+      EVENTS = %w[* create delete pull_request push].freeze
+
+      attr_reader :raw
+
+      delegate :id, :name, :active, to: :raw
+
+      def initialize(raw)
+        @raw = raw
+      end
+
+      def config
+        raw.config.attrs
+      end
+
+      def valid?
+        (EVENTS & raw.events).any? && active
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb
index 408d9b796325c3f2b5403d137e0dc73f3ba7003e..e5cf66a037102f70034b56ef765030722921ecf9 100644
--- a/lib/gitlab/github_import/importer.rb
+++ b/lib/gitlab/github_import/importer.rb
@@ -30,9 +30,8 @@ module Gitlab
       end
 
       def import_labels
-        client.labels(repo).each do |raw_data|
-          Label.create!(LabelFormatter.new(project, raw_data).attributes)
-        end
+        labels = client.labels(repo, per_page: 100)
+        labels.each { |raw| LabelFormatter.new(project, raw).create! }
 
         true
       rescue ActiveRecord::RecordInvalid => e
@@ -40,9 +39,8 @@ module Gitlab
       end
 
       def import_milestones
-        client.list_milestones(repo, state: :all).each do |raw_data|
-          Milestone.create!(MilestoneFormatter.new(project, raw_data).attributes)
-        end
+        milestones = client.milestones(repo, state: :all, per_page: 100)
+        milestones.each { |raw| MilestoneFormatter.new(project, raw).create! }
 
         true
       rescue ActiveRecord::RecordInvalid => e
@@ -50,16 +48,15 @@ module Gitlab
       end
 
       def import_issues
-        client.list_issues(repo, state: :all, sort: :created, direction: :asc).each do |raw_data|
-          gh_issue = IssueFormatter.new(project, raw_data)
+        issues = client.issues(repo, state: :all, sort: :created, direction: :asc, per_page: 100)
 
-          if gh_issue.valid?
-            issue = Issue.create!(gh_issue.attributes)
-            apply_labels(gh_issue.number, issue)
+        issues.each do |raw|
+          gh_issue = IssueFormatter.new(project, raw)
 
-            if gh_issue.has_comments?
-              import_comments(gh_issue.number, issue)
-            end
+          if gh_issue.valid?
+            issue = gh_issue.create!
+            apply_labels(issue)
+            import_comments(issue) if gh_issue.has_comments?
           end
         end
 
@@ -69,34 +66,48 @@ module Gitlab
       end
 
       def import_pull_requests
-        pull_requests = client.pull_requests(repo, state: :all, sort: :created, direction: :asc)
-                              .map { |raw| PullRequestFormatter.new(project, raw) }
-                              .select(&:valid?)
+        hooks = client.hooks(repo).map { |raw| HookFormatter.new(raw) }.select(&:valid?)
+        disable_webhooks(hooks)
+
+        pull_requests = client.pull_requests(repo, state: :all, sort: :created, direction: :asc, per_page: 100)
+        pull_requests = pull_requests.map { |raw| PullRequestFormatter.new(project, raw) }.select(&:valid?)
 
         source_branches_removed = pull_requests.reject(&:source_branch_exists?).map { |pr| [pr.source_branch_name, pr.source_branch_sha] }
         target_branches_removed = pull_requests.reject(&:target_branch_exists?).map { |pr| [pr.target_branch_name, pr.target_branch_sha] }
         branches_removed = source_branches_removed | target_branches_removed
 
-        create_refs(branches_removed)
+        restore_branches(branches_removed)
 
         pull_requests.each do |pull_request|
-          merge_request = MergeRequest.new(pull_request.attributes)
-
-          if merge_request.save
-            apply_labels(pull_request.number, merge_request)
-            import_comments(pull_request.number, merge_request)
-            import_comments_on_diff(pull_request.number, merge_request)
-          end
+          merge_request = pull_request.create!
+          apply_labels(merge_request)
+          import_comments(merge_request)
+          import_comments_on_diff(merge_request)
         end
 
-        delete_refs(branches_removed)
-
         true
       rescue ActiveRecord::RecordInvalid => e
         raise Projects::ImportService::Error, e.message
+      ensure
+        clean_up_restored_branches(branches_removed)
+        clean_up_disabled_webhooks(hooks)
+      end
+
+      def disable_webhooks(hooks)
+        update_webhooks(hooks, active: false)
+      end
+
+      def clean_up_disabled_webhooks(hooks)
+        update_webhooks(hooks, active: true)
+      end
+
+      def update_webhooks(hooks, options)
+        hooks.each do |hook|
+          client.edit_hook(repo, hook.id, hook.name, hook.config, options)
+        end
       end
 
-      def create_refs(branches)
+      def restore_branches(branches)
         branches.each do |name, sha|
           client.create_ref(repo, "refs/heads/#{name}", sha)
         end
@@ -104,15 +115,15 @@ module Gitlab
         project.repository.fetch_ref(repo_url, '+refs/heads/*', 'refs/heads/*')
       end
 
-      def delete_refs(branches)
+      def clean_up_restored_branches(branches)
         branches.each do |name, _|
           client.delete_ref(repo, "heads/#{name}")
           project.repository.rm_branch(project.creator, name)
         end
       end
 
-      def apply_labels(number, issuable)
-        issue = client.issue(repo, number)
+      def apply_labels(issuable)
+        issue = client.issue(repo, issuable.iid)
 
         if issue.labels.count > 0
           label_ids = issue.labels.map do |raw|
@@ -123,20 +134,20 @@ module Gitlab
         end
       end
 
-      def import_comments(issue_number, noteable)
-        comments = client.issue_comments(repo, issue_number)
-        create_comments(comments, noteable)
+      def import_comments(issuable)
+        comments = client.issue_comments(repo, issuable.iid, per_page: 100)
+        create_comments(issuable, comments)
       end
 
-      def import_comments_on_diff(pull_request_number, merge_request)
-        comments = client.pull_request_comments(repo, pull_request_number)
-        create_comments(comments, merge_request)
+      def import_comments_on_diff(merge_request)
+        comments = client.pull_request_comments(repo, merge_request.iid, per_page: 100)
+        create_comments(merge_request, comments)
       end
 
-      def create_comments(comments, noteable)
-        comments.each do |raw_data|
-          comment = CommentFormatter.new(project, raw_data)
-          noteable.notes.create!(comment.attributes)
+      def create_comments(issuable, comments)
+        comments.each do |raw|
+          comment = CommentFormatter.new(project, raw)
+          issuable.notes.create!(comment.attributes)
         end
       end
 
diff --git a/lib/gitlab/github_import/issue_formatter.rb b/lib/gitlab/github_import/issue_formatter.rb
index c8173913b4e3975044aa86ef0db97daf9250fb0e..835ec858b35c537dfd11c8c2146ea89fae2eec8e 100644
--- a/lib/gitlab/github_import/issue_formatter.rb
+++ b/lib/gitlab/github_import/issue_formatter.rb
@@ -20,6 +20,10 @@ module Gitlab
         raw_data.comments > 0
       end
 
+      def klass
+        Issue
+      end
+
       def number
         raw_data.number
       end
diff --git a/lib/gitlab/github_import/label_formatter.rb b/lib/gitlab/github_import/label_formatter.rb
index c2b9d40b511ab15e939644476300562101e394b4..9f18244e7d7a67ade7b56edcf184e8ea31d38ec8 100644
--- a/lib/gitlab/github_import/label_formatter.rb
+++ b/lib/gitlab/github_import/label_formatter.rb
@@ -9,6 +9,10 @@ module Gitlab
         }
       end
 
+      def klass
+        Label
+      end
+
       private
 
       def color
diff --git a/lib/gitlab/github_import/milestone_formatter.rb b/lib/gitlab/github_import/milestone_formatter.rb
index e91a7e328cfb0d6c92821c3911a56a61a12dedf5..53d4b3102d195a40fb6cad8bb68d3e3898458279 100644
--- a/lib/gitlab/github_import/milestone_formatter.rb
+++ b/lib/gitlab/github_import/milestone_formatter.rb
@@ -14,6 +14,10 @@ module Gitlab
         }
       end
 
+      def klass
+        Milestone
+      end
+
       private
 
       def number
diff --git a/lib/gitlab/github_import/pull_request_formatter.rb b/lib/gitlab/github_import/pull_request_formatter.rb
index 574737b31c1f7ad3da8e3947b790885ca4528263..498b00cb658d2d73845e942a3c20fc44bdf4d767 100644
--- a/lib/gitlab/github_import/pull_request_formatter.rb
+++ b/lib/gitlab/github_import/pull_request_formatter.rb
@@ -24,6 +24,10 @@ module Gitlab
         }
       end
 
+      def klass
+        MergeRequest
+      end
+
       def number
         raw_data.number
       end
@@ -79,10 +83,9 @@ module Gitlab
       end
 
       def state
-        @state ||= case true
-                   when raw_data.state == 'closed' && raw_data.merged_at.present?
+        @state ||= if raw_data.state == 'closed' && raw_data.merged_at.present?
                      'merged'
-                   when raw_data.state == 'closed'
+                   elsif raw_data.state == 'closed'
                      'closed'
                    else
                      'opened'
diff --git a/lib/gitlab/gitignore.rb b/lib/gitlab/gitignore.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f46b43b61a4e8efcb5ea777a23f3210b7eb6d614
--- /dev/null
+++ b/lib/gitlab/gitignore.rb
@@ -0,0 +1,56 @@
+module Gitlab
+  class Gitignore
+    FILTER_REGEX = /\.gitignore\z/.freeze
+
+    def initialize(path)
+      @path = path
+    end
+
+    def name
+      File.basename(@path, '.gitignore')
+    end
+
+    def content
+      File.read(@path)
+    end
+
+    class << self
+      def all
+        languages_frameworks + global
+      end
+
+      def find(key)
+        file_name = "#{key}.gitignore"
+
+        directory = select_directory(file_name)
+        directory ? new(File.join(directory, file_name)) : nil
+      end
+
+      def global
+        files_for_folder(global_dir).map { |file| new(File.join(global_dir, file)) }
+      end
+
+      def languages_frameworks
+        files_for_folder(gitignore_dir).map { |file| new(File.join(gitignore_dir, file)) }
+      end
+
+      private
+
+      def select_directory(file_name)
+        [gitignore_dir, global_dir].find { |dir| File.exist?(File.join(dir, file_name)) }
+      end
+
+      def global_dir
+        File.join(gitignore_dir, 'Global')
+      end
+
+      def gitignore_dir
+        Rails.root.join('vendor/gitignore')
+      end
+
+      def files_for_folder(dir)
+        Dir.glob("#{dir.to_s}/*.gitignore").map { |file| file.gsub(FILTER_REGEX, '') }
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/gitlab_import/importer.rb b/lib/gitlab/gitlab_import/importer.rb
index 3e51c06877ee2f4d5a565b059a83eac92e119936..3f76ec979778e142e8cc6bc12a45e90a144c34f9 100644
--- a/lib/gitlab/gitlab_import/importer.rb
+++ b/lib/gitlab/gitlab_import/importer.rb
@@ -5,9 +5,9 @@ module Gitlab
 
       def initialize(project)
         @project = project
-        credentials = project.import_data
-        if credentials && credentials[:password]
-          @client = Client.new(credentials[:password])
+        import_data = project.import_data
+        if import_data && import_data.credentials && import_data.credentials[:password]
+          @client = Client.new(import_data.credentials[:password])
           @formatter = Gitlab::ImportFormatter.new
         else
           raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}"
@@ -17,7 +17,7 @@ module Gitlab
       def execute
         project_identifier = CGI.escape(project.import_source)
 
-        #Issues && Comments
+        # Issues && Comments
         issues = client.issues(project_identifier)
 
         issues.each do |issue|
diff --git a/lib/gitlab/gl_id.rb b/lib/gitlab/gl_id.rb
new file mode 100644
index 0000000000000000000000000000000000000000..624fd00367e0f5c128497449630242824b62e416
--- /dev/null
+++ b/lib/gitlab/gl_id.rb
@@ -0,0 +1,11 @@
+module Gitlab
+  module GlId
+    def self.gl_id(user)
+      if user.present?
+        "user-#{user.id}"
+      else
+        ""
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index ab900b641c4973aac32752fce5355132c76909b4..f751a3a12fdf90c46533a4e9d2fe8c50a66a0a37 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -8,6 +8,7 @@ module Gitlab
       gon.relative_url_root      = Gitlab.config.gitlab.relative_url_root
       gon.shortcuts_path         = help_shortcuts_path
       gon.user_color_scheme      = Gitlab::ColorSchemes.for_user(current_user).css_class
+      gon.award_menu_url         = emojis_path
 
       if current_user
         gon.current_user_id = current_user.id
diff --git a/lib/gitlab/key_fingerprint.rb b/lib/gitlab/key_fingerprint.rb
index a792fa4b795919ef292ff74b17c2e4b473fa597a..f0e7f14a5373c89df3d89d7f2b084fff175c81f4 100644
--- a/lib/gitlab/key_fingerprint.rb
+++ b/lib/gitlab/key_fingerprint.rb
@@ -17,9 +17,9 @@ module Gitlab
         file.rewind
 
         cmd = []
-        cmd.push *%W(ssh-keygen)
-        cmd.push *%W(-E md5) if explicit_fingerprint_algorithm?
-        cmd.push *%W(-lf #{file.path})
+        cmd.push('ssh-keygen')
+        cmd.push('-E', 'md5') if explicit_fingerprint_algorithm?
+        cmd.push('-lf', file.path)
 
         cmd_output, cmd_status = popen(cmd, '/tmp')
       end
diff --git a/lib/gitlab/lazy.rb b/lib/gitlab/lazy.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2a659ae4c74c28c4a43da65689936a06707e8b81
--- /dev/null
+++ b/lib/gitlab/lazy.rb
@@ -0,0 +1,34 @@
+module Gitlab
+  # A class that can be wrapped around an expensive method call so it's only
+  # executed when actually needed.
+  #
+  # Usage:
+  #
+  #     object = Gitlab::Lazy.new { some_expensive_work_here }
+  #
+  #     object['foo']
+  #     object.bar
+  class Lazy < BasicObject
+    def initialize(&block)
+      @block = block
+    end
+
+    def method_missing(name, *args, &block)
+      __evaluate__
+
+      @result.__send__(name, *args, &block)
+    end
+
+    def respond_to_missing?(name, include_private = false)
+      __evaluate__
+
+      @result.respond_to?(name, include_private) || super
+    end
+
+    private
+
+    def __evaluate__
+      @result = @block.call unless defined?(@result)
+    end
+  end
+end
diff --git a/lib/gitlab/ldap/config.rb b/lib/gitlab/ldap/config.rb
index b930a34c09bc8a53ef8bdc5c87b2a7f0a7e2d172..206368ae7dd9c49707de07eb32bf625ab821ab42 100644
--- a/lib/gitlab/ldap/config.rb
+++ b/lib/gitlab/ldap/config.rb
@@ -101,6 +101,10 @@ module Gitlab
         options['timeout'].to_i
       end
 
+      def external_groups
+        options['external_groups']
+      end
+
       protected
 
       def base_config
diff --git a/lib/gitlab/ldap/group_sync.rb b/lib/gitlab/ldap/group_sync.rb
index 44775d08dc24f0eb2dfaa6c1fcbddf8b5c852795..88886759755d53d01cb8bb300ee4ebb86d9ba455 100644
--- a/lib/gitlab/ldap/group_sync.rb
+++ b/lib/gitlab/ldap/group_sync.rb
@@ -49,6 +49,14 @@ module Gitlab
           logger.debug { "No `admin_group` configured for '#{provider}' provider. Skipping" }
         end
 
+        if external_groups.empty?
+          logger.debug { "No `external_groups` configured for '#{provider}' provider. Skipping" }
+        else
+          logger.debug { "Syncing external users for '#{provider}' provider" }
+          sync_external_users
+          logger.debug { "Finished syncing external users for '#{provider}' provider" }
+        end
+
         nil
       end
 
@@ -121,6 +129,36 @@ module Gitlab
         end
       end
 
+      # Update external users based on the specified external groups CN
+      def sync_external_users
+        current_external_users = ::User.external.with_provider(provider)
+        verified_external_users = []
+
+        external_groups.each do |group|
+          group_dns = dns_for_group_cn(group)
+
+          group_dns.each do |member_dn|
+            user = Gitlab::LDAP::User.find_by_uid_and_provider(member_dn, provider)
+
+            if user.present?
+              user.external = true
+              user.save
+              verified_external_users << user
+            else
+              logger.debug do
+                <<-MSG.strip_heredoc.tr("\n", ' ')
+                  #{self.class.name}: User with DN `#{member_dn}` should be marked as
+                  external but there is no user in GitLab with that identity.
+                  Membership will be updated once the user signs in for the first time.
+                MSG
+              end
+            end
+          end
+        end
+
+        update_external_permissions(current_external_users, verified_external_users)
+      end
+
       private
 
       # Cache LDAP group member DNs so we don't query LDAP groups more than once.
@@ -151,6 +189,10 @@ module Gitlab
         config.admin_group
       end
 
+      def external_groups
+        config.external_groups
+      end
+
       def ldap_group_member_dns(ldap_group_cn)
         ldap_group = Gitlab::LDAP::Group.find_by_cn(ldap_group_cn, adapter)
         unless ldap_group.present?
@@ -180,7 +222,14 @@ module Gitlab
       # account for that. See gitlab-ee#442
       def ensure_full_dns!(dns)
         dns.map! do |dn|
-          parsed_dn = Net::LDAP::DN.new(dn).to_a
+          begin
+            parsed_dn = Net::LDAP::DN.new(dn).to_a
+          rescue RuntimeError => e
+            # Net::LDAP raises a generic RuntimeError. Bad library! Bad!
+            logger.error { "Found malformed DN: '#{dn}'. Skipping. #{e.message}" }
+            next
+          end
+
           # If there is more than one key/value set we must have a full DN,
           # or at least the probability is higher.
           if parsed_dn.count > 2
@@ -192,6 +241,9 @@ module Gitlab
             dn
           end
         end
+
+        # Remove `nil` values generated by the rescue above.
+        dns.compact!
       end
 
       def member_uid_to_dn(uid)
@@ -267,6 +319,16 @@ module Gitlab
         end
       end
 
+      def update_external_permissions(users, verified)
+        # Restore normal access to users no longer found in the external groups
+        users.each do |user|
+          unless verified.include?(user)
+            user.external = false
+            user.save
+          end
+        end
+      end
+
       def add_new_members(group, access_levels)
         logger.debug { "Adding new members to '#{group.name}' group" }
 
diff --git a/lib/gitlab/metrics/instrumentation.rb b/lib/gitlab/metrics/instrumentation.rb
index 0f115893a15e0e144735b1a2af7954c28e6a30fc..d81d26754fe004934a19cbcdd3b6f8d17d0bd981 100644
--- a/lib/gitlab/metrics/instrumentation.rb
+++ b/lib/gitlab/metrics/instrumentation.rb
@@ -56,7 +56,7 @@ module Gitlab
         end
       end
 
-      # Instruments all public methods of a module.
+      # Instruments all public and private methods of a module.
       #
       # This method optionally takes a block that can be used to determine if a
       # method should be instrumented or not. The block is passed the receiving
@@ -65,7 +65,8 @@ module Gitlab
       #
       # mod - The module to instrument.
       def self.instrument_methods(mod)
-        mod.public_methods(false).each do |name|
+        methods = mod.methods(false) + mod.private_methods(false)
+        methods.each do |name|
           method = mod.method(name)
 
           if method.owner == mod.singleton_class
@@ -76,13 +77,14 @@ module Gitlab
         end
       end
 
-      # Instruments all public instance methods of a module.
+      # Instruments all public and private instance methods of a module.
       #
       # See `instrument_methods` for more information.
       #
       # mod - The module to instrument.
       def self.instrument_instance_methods(mod)
-        mod.public_instance_methods(false).each do |name|
+        methods = mod.instance_methods(false) + mod.private_instance_methods(false)
+        methods.each do |name|
           method = mod.instance_method(name)
 
           if method.owner == mod
@@ -149,13 +151,16 @@ module Gitlab
             trans = Gitlab::Metrics::Instrumentation.transaction
 
             if trans
-              start    = Time.now
-              retval   = super
-              duration = (Time.now - start) * 1000.0
+              start     = Time.now
+              cpu_start = Gitlab::Metrics::System.cpu_time
+              retval    = super
+              duration  = (Time.now - start) * 1000.0
 
               if duration >= Gitlab::Metrics.method_call_threshold
+                cpu_duration = Gitlab::Metrics::System.cpu_time - cpu_start
+
                 trans.add_metric(Gitlab::Metrics::Instrumentation::SERIES,
-                                 { duration: duration },
+                                 { duration: duration, cpu_duration: cpu_duration },
                                  method: #{label.inspect})
               end
 
diff --git a/lib/gitlab/metrics/rack_middleware.rb b/lib/gitlab/metrics/rack_middleware.rb
index 6f179789d3e3016929df37a3a4e0e57456d9af68..3fe27779d03d861ff984f2fe6a4ba7ddfb5a4776 100644
--- a/lib/gitlab/metrics/rack_middleware.rb
+++ b/lib/gitlab/metrics/rack_middleware.rb
@@ -1,8 +1,9 @@
 module Gitlab
   module Metrics
-    # Rack middleware for tracking Rails requests.
+    # Rack middleware for tracking Rails and Grape requests.
     class RackMiddleware
       CONTROLLER_KEY = 'action_controller.instance'
+      ENDPOINT_KEY   = 'api.endpoint'
 
       def initialize(app)
         @app = app
@@ -21,6 +22,8 @@ module Gitlab
         ensure
           if env[CONTROLLER_KEY]
             tag_controller(trans, env)
+          elsif env[ENDPOINT_KEY]
+            tag_endpoint(trans, env)
           end
 
           trans.finish
@@ -42,6 +45,26 @@ module Gitlab
         controller   = env[CONTROLLER_KEY]
         trans.action = "#{controller.class.name}##{controller.action_name}"
       end
+
+      def tag_endpoint(trans, env)
+        endpoint = env[ENDPOINT_KEY]
+        path = endpoint_paths_cache[endpoint.route.route_method][endpoint.route.route_path]
+        trans.action = "Grape##{endpoint.route.route_method} #{path}"
+      end
+
+      private
+
+      def endpoint_paths_cache
+        @endpoint_paths_cache ||= Hash.new do |hash, http_method|
+          hash[http_method] = Hash.new do |inner_hash, raw_path|
+            inner_hash[raw_path] = endpoint_instrumentable_path(raw_path)
+          end
+        end
+      end
+
+      def endpoint_instrumentable_path(raw_path)
+        raw_path.sub('(.:format)', '').sub('/:version', '')
+      end
     end
   end
 end
diff --git a/lib/gitlab/metrics/sampler.rb b/lib/gitlab/metrics/sampler.rb
index fc709222a9b748b584b5865c86fda02fccf5dd70..0000450d9bb6f7ba7d47ef6a54ca67834de13729 100644
--- a/lib/gitlab/metrics/sampler.rb
+++ b/lib/gitlab/metrics/sampler.rb
@@ -66,7 +66,11 @@ module Gitlab
         def sample_objects
           sample = Allocations.to_hash
           counts = sample.each_with_object({}) do |(klass, count), hash|
-            hash[klass.name] = count
+            name = klass.name
+
+            next unless name
+
+            hash[name] = count
           end
 
           # Symbols aren't allocated so we'll need to add those manually.
diff --git a/lib/gitlab/middleware/go.rb b/lib/gitlab/middleware/go.rb
index 50b0dd32380325268496a65674056c676d30f4a8..5764ab1565279617829350145d1b020286745955 100644
--- a/lib/gitlab/middleware/go.rb
+++ b/lib/gitlab/middleware/go.rb
@@ -39,7 +39,7 @@ module Gitlab
         request_url = URI.join(base_url, project_path)
         domain_path = strip_url(request_url.to_s)
 
-        "<!DOCTYPE html><html><head><meta content='#{domain_path} git #{request_url}.git' name='go-import'></head></html>\n";
+        "<!DOCTYPE html><html><head><meta content='#{domain_path} git #{request_url}.git' name='go-import'></head></html>\n"
       end
 
       def strip_url(url)
diff --git a/lib/gitlab/middleware/rails_queue_duration.rb b/lib/gitlab/middleware/rails_queue_duration.rb
new file mode 100644
index 0000000000000000000000000000000000000000..56608b1b2765dd18b9c480d88d2b9689b503dd39
--- /dev/null
+++ b/lib/gitlab/middleware/rails_queue_duration.rb
@@ -0,0 +1,24 @@
+# This Rack middleware is intended to measure the latency between
+# gitlab-workhorse forwarding a request to the Rails application and the
+# time this middleware is reached.
+
+module Gitlab
+  module Middleware
+    class RailsQueueDuration
+      def initialize(app)
+        @app = app
+      end
+      
+      def call(env)
+        trans = Gitlab::Metrics.current_transaction
+        proxy_start = env['HTTP_GITLAB_WORHORSE_PROXY_START'].presence
+        if trans && proxy_start
+          # Time in milliseconds since gitlab-workhorse started the request
+          trans.set(:rails_queue_duration, Time.now.to_f * 1_000 - proxy_start.to_f / 1_000_000)
+        end
+
+        @app.call(env)
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb
index 356e96fcbab1aa3ad5ade8d8093c19ed4f0c44e5..78f3ecb4cb4b1453aea0b4127f3526b93c35d628 100644
--- a/lib/gitlab/o_auth/user.rb
+++ b/lib/gitlab/o_auth/user.rb
@@ -69,13 +69,20 @@ module Gitlab
         return unless ldap_person
 
         # If a corresponding person exists with same uid in a LDAP server,
-        # set up a Gitlab user with dual LDAP and Omniauth identities.
-        if user = Gitlab::LDAP::User.find_by_uid_and_provider(ldap_person.dn, ldap_person.provider)
-          # Case when a LDAP user already exists in Gitlab. Add the Omniauth identity to existing account.
+        # check if the user already has a GitLab account.
+        user = Gitlab::LDAP::User.find_by_uid_and_provider(ldap_person.dn, ldap_person.provider)
+        if user
+          # Case when a LDAP user already exists in Gitlab. Add the OAuth identity to existing account.
+          log.info "LDAP account found for user #{user.username}. Building new #{auth_hash.provider} identity."
           user.identities.build(extern_uid: auth_hash.uid, provider: auth_hash.provider)
         else
-          # No account in Gitlab yet: create it and add the LDAP identity
-          user = build_new_user
+          log.info "No existing LDAP account was found in GitLab. Checking for #{auth_hash.provider} account."
+          user = find_by_uid_and_provider
+          if user.nil?
+            log.info "No user found using #{auth_hash.provider} provider. Creating a new one."
+            user = build_new_user
+          end
+          log.info "Correct account has been found. Adding LDAP identity to user: #{user.username}."
           user.identities.new(provider: ldap_person.provider, extern_uid: ldap_person.dn)
         end
 
diff --git a/lib/gitlab/path_locks_finder.rb b/lib/gitlab/path_locks_finder.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c70db9c311eea927a2d99e68a040ea8afeeb861f
--- /dev/null
+++ b/lib/gitlab/path_locks_finder.rb
@@ -0,0 +1,58 @@
+# The database stores locked paths as following:
+# 'app/models/project.rb' or 'lib/gitlab'
+# To determine that 'lib/gitlab/some_class.rb' is locked we need to generate
+# tokens for every requested paths and check every token whether it exist in path locks table or not.
+# So for 'lib/gitlab/some_class.rb' path we would need to search next paths:
+# 'lib', 'lib/gitlab' and 'lib/gitlab/some_class.rb'
+# This class also implements a memoization for common paths like 'lib' 'lib/gitlab', 'app', etc.
+
+class Gitlab::PathLocksFinder
+  def initialize(project)
+    @project = project
+    @non_locked_paths = []
+  end
+
+  def get_lock_info(path, exact_match: false)
+    if exact_match
+      return find_lock(path)
+    else
+      tokenize(path).each do |token|
+        if lock = find_lock(token)
+          return lock
+        end
+      end
+
+      false
+    end
+  end
+
+  private
+
+  # This returns hierarchy tokens for path
+  # app/models/project.rb => ['app', 'app/models', 'app/models/project.rb']
+  def tokenize(path)
+    segments = path.split("/")
+
+    tokens = []
+    begin
+      tokens << segments.join("/")
+      segments.pop
+    end until segments.empty?
+
+    tokens
+  end
+
+  def find_lock(token)
+    if @non_locked_paths.include?(token)
+      return false
+    end
+
+    lock = @project.path_locks.find_by(path: token)
+
+    unless lock
+      @non_locked_paths << token
+    end
+
+    lock
+  end
+end
diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb
index 71c5b6801fb66ef94cba8c8533d530be688a17fb..183bd10d6a339b80de9aedea6d559c18fd7d6413 100644
--- a/lib/gitlab/project_search_results.rb
+++ b/lib/gitlab/project_search_results.rb
@@ -74,7 +74,7 @@ module Gitlab
     end
 
     def notes
-      project.notes.user.search(query).order('updated_at DESC')
+      project.notes.user.search(query, as_user: @current_user).order('updated_at DESC')
     end
 
     def commits
diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index 13c4d64c99b0d9ee59ed0ef73c2d87caa6b8d199..11c0b01f0dc06b53f9b94d7a2c443f39ef727a80 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -4,10 +4,9 @@ module Gitlab
     REFERABLES = %i(user issue label milestone merge_request snippet commit commit_range)
     attr_accessor :project, :current_user, :author
 
-    def initialize(project, current_user = nil, author = nil)
+    def initialize(project, current_user = nil)
       @project = project
       @current_user = current_user
-      @author = author
 
       @references = {}
 
@@ -18,17 +17,21 @@ module Gitlab
       super(text, context.merge(project: project))
     end
 
+    def references(type)
+      super(type, project, current_user)
+    end
+
     REFERABLES.each do |type|
       define_method("#{type}s") do
-        @references[type] ||= references(type, reference_context)
+        @references[type] ||= references(type)
       end
     end
 
     def issues
       if project && project.jira_tracker?
-        @references[:external_issue] ||= references(:external_issue, reference_context)
+        @references[:external_issue] ||= references(:external_issue)
       else
-        @references[:issue] ||= references(:issue, reference_context)
+        @references[:issue] ||= references(:issue)
       end
     end
 
@@ -46,11 +49,5 @@ module Gitlab
 
       @pattern = Regexp.union(patterns.compact)
     end
-
-    private
-
-    def reference_context
-      { project: project, current_user: current_user, author: author }
-    end
   end
 end
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index 1cbd6d945a0668f17ece55e3556a4115c4c8e6a6..c84c68f96f63714078fb915000dd525a445c91c6 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -100,5 +100,13 @@ module Gitlab
     def container_registry_reference_regex
       git_reference_regex
     end
+
+    def environment_name_regex
+      @environment_name_regex ||= /\A[a-zA-Z0-9_-]+\z/.freeze
+    end
+
+    def environment_name_regex_message
+      "can contain only letters, digits, '-' and '_'."
+    end
   end
 end
diff --git a/lib/gitlab/saml/user.rb b/lib/gitlab/saml/user.rb
index 37c94be8a0246af8511867b8d84ee06830ba6b5e..ebfde9038b181cb4f08235b2afbd39322e72a76f 100644
--- a/lib/gitlab/saml/user.rb
+++ b/lib/gitlab/saml/user.rb
@@ -12,12 +12,12 @@ module Gitlab
       end
 
       def gl_user
-        @user ||= find_by_uid_and_provider
-
         if auto_link_ldap_user?
           @user ||= find_or_create_ldap_user
         end
 
+        @user ||= find_by_uid_and_provider
+
         if auto_link_saml_user?
           @user ||= find_by_email
         end
diff --git a/lib/gitlab/sanitizers/svg.rb b/lib/gitlab/sanitizers/svg.rb
index 5e95f6c05290ec35788eb8bc5674c84304072dd1..8304b9a482c36b40c9dd841abdd73cc13e9bcf0a 100644
--- a/lib/gitlab/sanitizers/svg.rb
+++ b/lib/gitlab/sanitizers/svg.rb
@@ -12,23 +12,45 @@ module Gitlab
         def scrub(node)
           unless Whitelist::ALLOWED_ELEMENTS.include?(node.name)
             node.unlink
-          else
-            node.attributes.each do |attr_name, attr|
-              valid_attributes = Whitelist::ALLOWED_ATTRIBUTES[node.name]
-
-              unless valid_attributes && valid_attributes.include?(attr_name)
-                if Whitelist::ALLOWED_DATA_ATTRIBUTES_IN_ELEMENTS.include?(node.name) &&
-                    attr_name.start_with?('data-')
-                  # Arbitrary data attributes are allowed. Verify that the attribute
-                  # is a valid data attribute.
-                  attr.unlink unless attr_name =~ DATA_ATTR_PATTERN
-                else
-                  attr.unlink
-                end
+            return
+          end
+
+          valid_attributes = Whitelist::ALLOWED_ATTRIBUTES[node.name]
+          return unless valid_attributes
+
+          node.attribute_nodes.each do |attr|
+            attr_name = attribute_name_with_namespace(attr)
+
+            if valid_attributes.include?(attr_name)
+              attr.unlink if unsafe_href?(attr)
+            else
+              # Arbitrary data attributes are allowed.
+              unless allows_data_attribute?(node) && data_attribute?(attr)
+                attr.unlink
               end
             end
           end
         end
+
+        def attribute_name_with_namespace(attr)
+          if attr.namespace
+            "#{attr.namespace.prefix}:#{attr.name}"
+          else
+            attr.name
+          end
+        end
+
+        def allows_data_attribute?(node)
+          Whitelist::ALLOWED_DATA_ATTRIBUTES_IN_ELEMENTS.include?(node.name)
+        end
+
+        def unsafe_href?(attr)
+          attribute_name_with_namespace(attr) == 'xlink:href' && !attr.value.start_with?('#')
+        end
+
+        def data_attribute?(attr)
+          attr.name.start_with?('data-') && attr.name =~ DATA_ATTR_PATTERN && attr.namespace.nil?
+        end
       end
     end
   end
diff --git a/lib/gitlab/seeder.rb b/lib/gitlab/seeder.rb
index 2ef0e982256cdefa8f60090221d4534053e2ed6e..7cf506ebe640d6c7f63978689c03ae55ac8557f7 100644
--- a/lib/gitlab/seeder.rb
+++ b/lib/gitlab/seeder.rb
@@ -5,7 +5,7 @@ module Gitlab
       SeedFu.quiet = true
       yield
       SeedFu.quiet = false
-      puts "\nOK".green
+      puts "\nOK".color(:green)
     end
 
     def self.by_user(user)
diff --git a/lib/gitlab/url_sanitizer.rb b/lib/gitlab/url_sanitizer.rb
index c59d53b941a7423ab7237d5bf87d736e6e5c13b7..7d02fe3c971e9d932a4e2aac4add2fb89bd99a22 100644
--- a/lib/gitlab/url_sanitizer.rb
+++ b/lib/gitlab/url_sanitizer.rb
@@ -7,7 +7,7 @@ module Gitlab
     end
 
     def initialize(url, credentials: nil)
-      @url = Addressable::URI.parse(URI.encode(url))
+      @url = Addressable::URI.parse(url)
       @credentials = credentials
     end
 
diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb
index a1ee1cba216bcdb8cb55f25b376c7b795b102620..9462f3368e672ae348bcbc2df6a6cf62936c3e79 100644
--- a/lib/gitlab/visibility_level.rb
+++ b/lib/gitlab/visibility_level.rb
@@ -32,6 +32,13 @@ module Gitlab
         }
       end
 
+      def highest_allowed_level
+        restricted_levels = current_application_settings.restricted_visibility_levels
+
+        allowed_levels = self.values - restricted_levels
+        allowed_levels.max || PRIVATE
+      end
+
       def allowed_for?(user, level)
         user.is_admin? || allowed_level?(level.to_i)
       end
diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb
index c3ddd4c268070be05704c6ddafcff041d7a05b43..40e8299c36b36d5ba5e430eb11371c1d786e66be 100644
--- a/lib/gitlab/workhorse.rb
+++ b/lib/gitlab/workhorse.rb
@@ -6,6 +6,13 @@ module Gitlab
     SEND_DATA_HEADER = 'Gitlab-Workhorse-Send-Data'
 
     class << self
+      def git_http_ok(repository, user)
+        {
+          'GL_ID' => Gitlab::GlId.gl_id(user),
+          'RepoPath' => repository.path_to_repo,
+        }
+      end
+
       def send_git_blob(repository, blob)
         params = {
           'RepoPath' => repository.path_to_repo,
@@ -14,24 +21,39 @@ module Gitlab
 
         [
           SEND_DATA_HEADER,
-          "git-blob:#{encode(params)}",
+          "git-blob:#{encode(params)}"
         ]
       end
 
-      def send_git_archive(project, ref, format)
+      def send_git_archive(repository, ref:, format:)
         format ||= 'tar.gz'
         format.downcase!
-        params = project.repository.archive_metadata(ref, Gitlab.config.gitlab.repository_downloads_path, format)
+        params = repository.archive_metadata(ref, Gitlab.config.gitlab.repository_downloads_path, format)
         raise "Repository or ref not found" if params.empty?
 
         [
           SEND_DATA_HEADER,
-          "git-archive:#{encode(params)}",
+          "git-archive:#{encode(params)}"
         ]
       end
-      
+
+      def send_git_diff(repository, diff_refs)
+        from, to = diff_refs
+
+        params = {
+          'RepoPath'  => repository.path_to_repo,
+          'ShaFrom'   => from.sha,
+          'ShaTo'     => to.sha
+        }
+
+        [
+          SEND_DATA_HEADER,
+          "git-diff:#{encode(params)}"
+        ]
+      end
+
       protected
-      
+
       def encode(hash)
         Base64.urlsafe_encode64(JSON.dump(hash))
       end
diff --git a/lib/support/nginx/registry-ssl b/lib/support/nginx/registry-ssl
new file mode 100644
index 0000000000000000000000000000000000000000..92511e268615f4c181339ecf802f8641b9310cee
--- /dev/null
+++ b/lib/support/nginx/registry-ssl
@@ -0,0 +1,53 @@
+## Lines starting with two hashes (##) are comments with information.
+## Lines starting with one hash (#) are configuration parameters that can be uncommented.
+##
+###################################
+##         configuration         ##
+###################################
+
+## Redirects all HTTP traffic to the HTTPS host
+server {
+  listen *:80;
+  server_name  registry.gitlab.example.com;
+  server_tokens off; ## Don't show the nginx version number, a security best practice
+  return 301 https://$http_host:$request_uri;
+  access_log  /var/log/nginx/gitlab_registry_access.log gitlab_access;
+  error_log   /var/log/nginx/gitlab_registry_error.log;
+}
+
+server {
+  # If a different port is specified in https://gitlab.com/gitlab-org/gitlab-ce/blob/8-8-stable/config/gitlab.yml.example#L182,
+  # it should be declared here as well
+  listen *:443 ssl http2;
+  server_name  registry.gitlab.example.com;
+  server_tokens off; ## Don't show the nginx version number, a security best practice
+
+  client_max_body_size 0;
+  chunked_transfer_encoding on;
+
+  ## Strong SSL Security
+  ## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/
+  ssl on;
+  ssl_certificate /etc/gitlab/ssl/registry.gitlab.example.com.crt
+  ssl_certificate_key /etc/gitlab/ssl/registry.gitlab.example.com.key
+
+  ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4';
+  ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
+  ssl_prefer_server_ciphers on;
+  ssl_session_cache  builtin:1000  shared:SSL:10m;
+  ssl_session_timeout  5m;
+
+  access_log  /var/log/gitlab/nginx/gitlab_registry_access.log gitlab_access;
+  error_log   /var/log/gitlab/nginx/gitlab_registry_error.log;
+
+  location / {
+    proxy_set_header  Host              $http_host;   # required for docker client's sake
+    proxy_set_header  X-Real-IP         $remote_addr; # pass on real client's IP
+    proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
+    proxy_set_header  X-Forwarded-Proto $scheme;
+    proxy_read_timeout                  900;
+
+    proxy_pass          http://localhost:5000;
+  }
+
+}
diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake
index f317e52816ff80b96b5382f7620ae6fedd93b89c..e2b77d2c83e3a3857acfefb0d74c94fe3ab0be1b 100644
--- a/lib/tasks/gitlab/backup.rake
+++ b/lib/tasks/gitlab/backup.rake
@@ -41,14 +41,14 @@ namespace :gitlab do
             removed.
           MSG
           ask_to_continue
-          puts 'Removing all tables. Press `Ctrl-C` within 5 seconds to abort'.yellow
+          puts 'Removing all tables. Press `Ctrl-C` within 5 seconds to abort'.color(:yellow)
           sleep(5)
         end
         # Drop all tables Load the schema to ensure we don't have any newer tables
         # hanging out from a failed upgrade
-        $progress.puts 'Cleaning the database ... '.blue
+        $progress.puts 'Cleaning the database ... '.color(:blue)
         Rake::Task['gitlab:db:drop_tables'].invoke
-        $progress.puts 'done'.green
+        $progress.puts 'done'.color(:green)
         Rake::Task['gitlab:backup:db:restore'].invoke
       end
       Rake::Task['gitlab:backup:repo:restore'].invoke unless backup.skipped?('repositories')
@@ -65,153 +65,161 @@ namespace :gitlab do
 
     namespace :repo do
       task create: :environment do
-        $progress.puts "Dumping repositories ...".blue
+        $progress.puts "Dumping repositories ...".color(:blue)
 
         if ENV["SKIP"] && ENV["SKIP"].include?("repositories")
-          $progress.puts "[SKIPPED]".cyan
+          $progress.puts "[SKIPPED]".color(:cyan)
         else
           Backup::Repository.new.dump
-          $progress.puts "done".green
+          $progress.puts "done".color(:green)
         end
       end
 
       task restore: :environment do
-        $progress.puts "Restoring repositories ...".blue
+        $progress.puts "Restoring repositories ...".color(:blue)
         Backup::Repository.new.restore
-        $progress.puts "done".green
+        $progress.puts "done".color(:green)
       end
     end
 
     namespace :db do
       task create: :environment do
-        $progress.puts "Dumping database ... ".blue
+        $progress.puts "Dumping database ... ".color(:blue)
 
         if ENV["SKIP"] && ENV["SKIP"].include?("db")
-          $progress.puts "[SKIPPED]".cyan
+          $progress.puts "[SKIPPED]".color(:cyan)
         else
           Backup::Database.new.dump
-          $progress.puts "done".green
+          $progress.puts "done".color(:green)
         end
       end
 
       task restore: :environment do
-        $progress.puts "Restoring database ... ".blue
+        $progress.puts "Restoring database ... ".color(:blue)
         Backup::Database.new.restore
-        $progress.puts "done".green
+        $progress.puts "done".color(:green)
       end
     end
 
     namespace :builds do
       task create: :environment do
-        $progress.puts "Dumping builds ... ".blue
+        $progress.puts "Dumping builds ... ".color(:blue)
 
         if ENV["SKIP"] && ENV["SKIP"].include?("builds")
-          $progress.puts "[SKIPPED]".cyan
+          $progress.puts "[SKIPPED]".color(:cyan)
         else
           Backup::Builds.new.dump
-          $progress.puts "done".green
+          $progress.puts "done".color(:green)
         end
       end
 
       task restore: :environment do
-        $progress.puts "Restoring builds ... ".blue
+        $progress.puts "Restoring builds ... ".color(:blue)
         Backup::Builds.new.restore
-        $progress.puts "done".green
+        $progress.puts "done".color(:green)
       end
     end
 
     namespace :uploads do
       task create: :environment do
-        $progress.puts "Dumping uploads ... ".blue
+        $progress.puts "Dumping uploads ... ".color(:blue)
 
         if ENV["SKIP"] && ENV["SKIP"].include?("uploads")
-          $progress.puts "[SKIPPED]".cyan
+          $progress.puts "[SKIPPED]".color(:cyan)
         else
           Backup::Uploads.new.dump
-          $progress.puts "done".green
+          $progress.puts "done".color(:green)
         end
       end
 
       task restore: :environment do
-        $progress.puts "Restoring uploads ... ".blue
+        $progress.puts "Restoring uploads ... ".color(:blue)
         Backup::Uploads.new.restore
-        $progress.puts "done".green
+        $progress.puts "done".color(:green)
       end
     end
 
     namespace :artifacts do
       task create: :environment do
-        $progress.puts "Dumping artifacts ... ".blue
+        $progress.puts "Dumping artifacts ... ".color(:blue)
 
         if ENV["SKIP"] && ENV["SKIP"].include?("artifacts")
-          $progress.puts "[SKIPPED]".cyan
+          $progress.puts "[SKIPPED]".color(:cyan)
         else
           Backup::Artifacts.new.dump
-          $progress.puts "done".green
+          $progress.puts "done".color(:green)
         end
       end
 
       task restore: :environment do
-        $progress.puts "Restoring artifacts ... ".blue
+        $progress.puts "Restoring artifacts ... ".color(:blue)
         Backup::Artifacts.new.restore
-        $progress.puts "done".green
+        $progress.puts "done".color(:green)
       end
     end
 
     namespace :pages do
       task create: :environment do
-        $progress.puts "Dumping pages ... ".blue
+        $progress.puts "Dumping pages ... ".color(:blue)
 
         if ENV["SKIP"] && ENV["SKIP"].include?("pages")
-          $progress.puts "[SKIPPED]".cyan
+          $progress.puts "[SKIPPED]".color(:cyan)
         else
           Backup::Pages.new.dump
-          $progress.puts "done".green
+          $progress.puts "done".color(:green)
         end
       end
 
       task restore: :environment do
-        $progress.puts "Restoring pages ... ".blue
+        $progress.puts "Restoring pages ... ".color(:blue)
         Backup::Pages.new.restore
-        $progress.puts "done".green
+        $progress.puts "done".color(:green)
       end
     end
 
     namespace :lfs do
       task create: :environment do
-        $progress.puts "Dumping lfs objects ... ".blue
+        $progress.puts "Dumping lfs objects ... ".color(:blue)
 
         if ENV["SKIP"] && ENV["SKIP"].include?("lfs")
-          $progress.puts "[SKIPPED]".cyan
+          $progress.puts "[SKIPPED]".color(:cyan)
         else
           Backup::Lfs.new.dump
-          $progress.puts "done".green
+          $progress.puts "done".color(:green)
         end
       end
 
       task restore: :environment do
-        $progress.puts "Restoring lfs objects ... ".blue
+        $progress.puts "Restoring lfs objects ... ".color(:blue)
         Backup::Lfs.new.restore
-        $progress.puts "done".green
+        $progress.puts "done".color(:green)
       end
     end
 
     namespace :registry do
       task create: :environment do
-        $progress.puts "Dumping container registry images ... ".blue
-
-        if ENV["SKIP"] && ENV["SKIP"].include?("registry")
-          $progress.puts "[SKIPPED]".cyan
+        $progress.puts "Dumping container registry images ... ".color(:blue)
+
+        if Gitlab.config.registry.enabled
+          if ENV["SKIP"] && ENV["SKIP"].include?("registry")
+            $progress.puts "[SKIPPED]".color(:cyan)
+          else
+            Backup::Registry.new.dump
+            $progress.puts "done".color(:green)
+          end
         else
-          Backup::Registry.new.dump
-          $progress.puts "done".green
+          $progress.puts "[DISABLED]".color(:cyan)
         end
       end
 
       task restore: :environment do
-        $progress.puts "Restoring container registry images ... ".blue
-        Backup::Registry.new.restore
-        $progress.puts "done".green
+        $progress.puts "Restoring container registry images ... ".color(:blue)
+        if Gitlab.config.registry.enabled
+          Backup::Registry.new.restore
+          $progress.puts "done".color(:green)
+        else
+          $progress.puts "[DISABLED]".color(:cyan)
+        end
       end
     end
 
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index a26f5812992d149e1a9e4af5abd4675eb9a43a03..c8c24bc7995e67dad4ab9bc8a8973f22658bae4e 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -30,7 +30,7 @@ namespace :gitlab do
       check_ruby_version
       check_git_version
       check_active_users
-      check_elasticsearch if Gitlab.config.elasticsearch.enabled
+      check_elasticsearch if ApplicationSetting.current.elasticsearch_indexing?
 
       finished_checking "GitLab"
     end
@@ -51,14 +51,14 @@ namespace :gitlab do
       end
 
       if correct_options.all?
-        puts "yes".green
+        puts "yes".color(:green)
       else
         print "Trying to fix Git error automatically. ..."
 
         if auto_fix_git_config(options)
-          puts "Success".green
+          puts "Success".color(:green)
         else
-          puts "Failed".red
+          puts "Failed".color(:red)
           try_fixing_it(
             sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global core.autocrlf \"#{options["core.autocrlf"]}\"")
           )
@@ -75,9 +75,9 @@ namespace :gitlab do
       database_config_file = Rails.root.join("config", "database.yml")
 
       if File.exists?(database_config_file)
-        puts "yes".green
+        puts "yes".color(:green)
       else
-        puts "no".red
+        puts "no".color(:red)
         try_fixing_it(
           "Copy config/database.yml.<your db> to config/database.yml",
           "Check that the information in config/database.yml is correct"
@@ -96,9 +96,9 @@ namespace :gitlab do
       gitlab_config_file = Rails.root.join("config", "gitlab.yml")
 
       if File.exists?(gitlab_config_file)
-        puts "yes".green
+        puts "yes".color(:green)
       else
-        puts "no".red
+        puts "no".color(:red)
         try_fixing_it(
           "Copy config/gitlab.yml.example to config/gitlab.yml",
           "Update config/gitlab.yml to match your setup"
@@ -115,14 +115,14 @@ namespace :gitlab do
 
       gitlab_config_file = Rails.root.join("config", "gitlab.yml")
       unless File.exists?(gitlab_config_file)
-        puts "can't check because of previous errors".magenta
+        puts "can't check because of previous errors".color(:magenta)
       end
 
       # omniauth or ldap could have been deleted from the file
       unless Gitlab.config['git_host']
-        puts "no".green
+        puts "no".color(:green)
       else
-        puts "yes".red
+        puts "yes".color(:red)
         try_fixing_it(
           "Backup your config/gitlab.yml",
           "Copy config/gitlab.yml.example to config/gitlab.yml",
@@ -139,16 +139,16 @@ namespace :gitlab do
       print "Init script exists? ... "
 
       if omnibus_gitlab?
-        puts 'skipped (omnibus-gitlab has no init script)'.magenta
+        puts 'skipped (omnibus-gitlab has no init script)'.color(:magenta)
         return
       end
 
       script_path = "/etc/init.d/gitlab"
 
       if File.exists?(script_path)
-        puts "yes".green
+        puts "yes".color(:green)
       else
-        puts "no".red
+        puts "no".color(:red)
         try_fixing_it(
           "Install the init script"
         )
@@ -163,7 +163,7 @@ namespace :gitlab do
       print "Init script up-to-date? ... "
 
       if omnibus_gitlab?
-        puts 'skipped (omnibus-gitlab has no init script)'.magenta
+        puts 'skipped (omnibus-gitlab has no init script)'.color(:magenta)
         return
       end
 
@@ -171,7 +171,7 @@ namespace :gitlab do
       script_path = "/etc/init.d/gitlab"
 
       unless File.exists?(script_path)
-        puts "can't check because of previous errors".magenta
+        puts "can't check because of previous errors".color(:magenta)
         return
       end
 
@@ -179,9 +179,9 @@ namespace :gitlab do
       script_content = File.read(script_path)
 
       if recipe_content == script_content
-        puts "yes".green
+        puts "yes".color(:green)
       else
-        puts "no".red
+        puts "no".color(:red)
         try_fixing_it(
           "Redownload the init script"
         )
@@ -198,9 +198,9 @@ namespace :gitlab do
       migration_status, _ = Gitlab::Popen.popen(%W(bundle exec rake db:migrate:status))
 
       unless migration_status =~ /down\s+\d{14}/
-        puts "yes".green
+        puts "yes".color(:green)
       else
-        puts "no".red
+        puts "no".color(:red)
         try_fixing_it(
           sudo_gitlab("bundle exec rake db:migrate RAILS_ENV=production")
         )
@@ -211,13 +211,13 @@ namespace :gitlab do
     def check_orphaned_group_members
       print "Database contains orphaned GroupMembers? ... "
       if GroupMember.where("user_id not in (select id from users)").count > 0
-        puts "yes".red
+        puts "yes".color(:red)
         try_fixing_it(
           "You can delete the orphaned records using something along the lines of:",
           sudo_gitlab("bundle exec rails runner -e production 'GroupMember.where(\"user_id NOT IN (SELECT id FROM users)\").delete_all'")
         )
       else
-        puts "no".green
+        puts "no".color(:green)
       end
     end
 
@@ -227,9 +227,9 @@ namespace :gitlab do
       log_path = Rails.root.join("log")
 
       if File.writable?(log_path)
-        puts "yes".green
+        puts "yes".color(:green)
       else
-        puts "no".red
+        puts "no".color(:red)
         try_fixing_it(
           "sudo chown -R gitlab #{log_path}",
           "sudo chmod -R u+rwX #{log_path}"
@@ -247,9 +247,9 @@ namespace :gitlab do
       tmp_path = Rails.root.join("tmp")
 
       if File.writable?(tmp_path)
-        puts "yes".green
+        puts "yes".color(:green)
       else
-        puts "no".red
+        puts "no".color(:red)
         try_fixing_it(
           "sudo chown -R gitlab #{tmp_path}",
           "sudo chmod -R u+rwX #{tmp_path}"
@@ -265,7 +265,7 @@ namespace :gitlab do
       print "Uploads directory setup correctly? ... "
 
       unless File.directory?(Rails.root.join('public/uploads'))
-        puts "no".red
+        puts "no".color(:red)
         try_fixing_it(
           "sudo -u #{gitlab_user} mkdir #{Rails.root}/public/uploads"
         )
@@ -281,16 +281,16 @@ namespace :gitlab do
 
       if File.stat(upload_path).mode == 040700
         unless Dir.exists?(upload_path_tmp)
-          puts 'skipped (no tmp uploads folder yet)'.magenta
+          puts 'skipped (no tmp uploads folder yet)'.color(:magenta)
           return
         end
 
         # If tmp upload dir has incorrect permissions, assume others do as well
         # Verify drwx------ permissions
         if File.stat(upload_path_tmp).mode == 040700 && File.owned?(upload_path_tmp)
-          puts "yes".green
+          puts "yes".color(:green)
         else
-          puts "no".red
+          puts "no".color(:red)
           try_fixing_it(
             "sudo chown -R #{gitlab_user} #{upload_path}",
             "sudo find #{upload_path} -type f -exec chmod 0644 {} \\;",
@@ -302,7 +302,7 @@ namespace :gitlab do
           fix_and_rerun
         end
       else
-        puts "no".red
+        puts "no".color(:red)
         try_fixing_it(
           "sudo chmod 700 #{upload_path}"
         )
@@ -321,9 +321,9 @@ namespace :gitlab do
       redis_version = redis_version.try(:match, /redis-cli (\d+\.\d+\.\d+)/)
       if redis_version &&
           (Gem::Version.new(redis_version[1]) > Gem::Version.new(min_redis_version))
-        puts "yes".green
+        puts "yes".color(:green)
       else
-        puts "no".red
+        puts "no".color(:red)
         try_fixing_it(
           "Update your redis server to a version >= #{min_redis_version}"
         )
@@ -362,10 +362,10 @@ namespace :gitlab do
       repo_base_path = Gitlab.config.gitlab_shell.repos_path
 
       if File.exists?(repo_base_path)
-        puts "yes".green
+        puts "yes".color(:green)
       else
-        puts "no".red
-        puts "#{repo_base_path} is missing".red
+        puts "no".color(:red)
+        puts "#{repo_base_path} is missing".color(:red)
         try_fixing_it(
           "This should have been created when setting up GitLab Shell.",
           "Make sure it's set correctly in config/gitlab.yml",
@@ -383,14 +383,14 @@ namespace :gitlab do
 
       repo_base_path = Gitlab.config.gitlab_shell.repos_path
       unless File.exists?(repo_base_path)
-        puts "can't check because of previous errors".magenta
+        puts "can't check because of previous errors".color(:magenta)
         return
       end
 
       unless File.symlink?(repo_base_path)
-        puts "no".green
+        puts "no".color(:green)
       else
-        puts "yes".red
+        puts "yes".color(:red)
         try_fixing_it(
           "Make sure it's set to the real directory in config/gitlab.yml"
         )
@@ -403,14 +403,14 @@ namespace :gitlab do
 
       repo_base_path = Gitlab.config.gitlab_shell.repos_path
       unless File.exists?(repo_base_path)
-        puts "can't check because of previous errors".magenta
+        puts "can't check because of previous errors".color(:magenta)
         return
       end
 
       if File.stat(repo_base_path).mode.to_s(8).ends_with?("2770")
-        puts "yes".green
+        puts "yes".color(:green)
       else
-        puts "no".red
+        puts "no".color(:red)
         try_fixing_it(
           "sudo chmod -R ug+rwX,o-rwx #{repo_base_path}",
           "sudo chmod -R ug-s #{repo_base_path}",
@@ -430,17 +430,17 @@ namespace :gitlab do
 
       repo_base_path = Gitlab.config.gitlab_shell.repos_path
       unless File.exists?(repo_base_path)
-        puts "can't check because of previous errors".magenta
+        puts "can't check because of previous errors".color(:magenta)
         return
       end
 
       uid = uid_for(gitlab_shell_ssh_user)
       gid = gid_for(gitlab_shell_owner_group)
       if File.stat(repo_base_path).uid == uid && File.stat(repo_base_path).gid == gid
-        puts "yes".green
+        puts "yes".color(:green)
       else
-        puts "no".red
-        puts "  User id for #{gitlab_shell_ssh_user}: #{uid}. Groupd id for #{gitlab_shell_owner_group}: #{gid}".blue
+        puts "no".color(:red)
+        puts "  User id for #{gitlab_shell_ssh_user}: #{uid}. Groupd id for #{gitlab_shell_owner_group}: #{gid}".color(:blue)
         try_fixing_it(
           "sudo chown -R #{gitlab_shell_ssh_user}:#{gitlab_shell_owner_group} #{repo_base_path}"
         )
@@ -457,7 +457,7 @@ namespace :gitlab do
       gitlab_shell_hooks_path = Gitlab.config.gitlab_shell.hooks_path
 
       unless Project.count > 0
-        puts "can't check, you have no projects".magenta
+        puts "can't check, you have no projects".color(:magenta)
         return
       end
       puts ""
@@ -467,12 +467,12 @@ namespace :gitlab do
         project_hook_directory = File.join(project.repository.path_to_repo, "hooks")
 
         if project.empty_repo?
-          puts "repository is empty".magenta
+          puts "repository is empty".color(:magenta)
         elsif File.directory?(project_hook_directory) && File.directory?(gitlab_shell_hooks_path) &&
             (File.realpath(project_hook_directory) == File.realpath(gitlab_shell_hooks_path))
-          puts 'ok'.green
+          puts 'ok'.color(:green)
         else
-          puts "wrong or missing hooks".red
+          puts "wrong or missing hooks".color(:red)
           try_fixing_it(
             sudo_gitlab("#{File.join(gitlab_shell_path, 'bin/create-hooks')}"),
             'Check the hooks_path in config/gitlab.yml',
@@ -492,9 +492,9 @@ namespace :gitlab do
       check_cmd = File.expand_path('bin/check', gitlab_shell_repo_base)
       puts "Running #{check_cmd}"
       if system(check_cmd, chdir: gitlab_shell_repo_base)
-        puts 'gitlab-shell self-check successful'.green
+        puts 'gitlab-shell self-check successful'.color(:green)
       else
-        puts 'gitlab-shell self-check failed'.red
+        puts 'gitlab-shell self-check failed'.color(:red)
         try_fixing_it(
           'Make sure GitLab is running;',
           'Check the gitlab-shell configuration file:',
@@ -508,7 +508,7 @@ namespace :gitlab do
       print "projects have namespace: ... "
 
       unless Project.count > 0
-        puts "can't check, you have no projects".magenta
+        puts "can't check, you have no projects".color(:magenta)
         return
       end
       puts ""
@@ -517,9 +517,9 @@ namespace :gitlab do
         print sanitized_message(project)
 
         if project.namespace
-          puts "yes".green
+          puts "yes".color(:green)
         else
-          puts "no".red
+          puts "no".color(:red)
           try_fixing_it(
             "Migrate global projects"
           )
@@ -577,9 +577,9 @@ namespace :gitlab do
       print "Running? ... "
 
       if sidekiq_process_count > 0
-        puts "yes".green
+        puts "yes".color(:green)
       else
-        puts "no".red
+        puts "no".color(:red)
         try_fixing_it(
           sudo_gitlab("RAILS_ENV=production bin/background_jobs start")
         )
@@ -597,9 +597,9 @@ namespace :gitlab do
 
       print 'Number of Sidekiq processes ... '
       if process_count == 1
-        puts '1'.green
+        puts '1'.color(:green)
       else
-        puts "#{process_count}".red
+        puts "#{process_count}".color(:red)
         try_fixing_it(
           'sudo service gitlab stop',
           "sudo pkill -u #{gitlab_user} -f sidekiq",
@@ -647,16 +647,16 @@ namespace :gitlab do
       print "Init.d configured correctly? ... "
 
       if omnibus_gitlab?
-        puts 'skipped (omnibus-gitlab has no init script)'.magenta
+        puts 'skipped (omnibus-gitlab has no init script)'.color(:magenta)
         return
       end
 
       path = "/etc/default/gitlab"
 
       if File.exist?(path) && File.read(path).include?("mail_room_enabled=true")
-        puts "yes".green
+        puts "yes".color(:green)
       else
-        puts "no".red
+        puts "no".color(:red)
         try_fixing_it(
           "Enable mail_room in the init.d configuration."
         )
@@ -673,9 +673,9 @@ namespace :gitlab do
       path = Rails.root.join("Procfile")
 
       if File.exist?(path) && File.read(path) =~ /^mail_room:/
-        puts "yes".green
+        puts "yes".color(:green)
       else
-        puts "no".red
+        puts "no".color(:red)
         try_fixing_it(
           "Enable mail_room in your Procfile."
         )
@@ -692,14 +692,14 @@ namespace :gitlab do
       path = "/etc/default/gitlab"
 
       unless File.exist?(path) && File.read(path).include?("mail_room_enabled=true")
-        puts "can't check because of previous errors".magenta
+        puts "can't check because of previous errors".color(:magenta)
         return
       end
 
       if mail_room_running?
-        puts "yes".green
+        puts "yes".color(:green)
       else
-        puts "no".red
+        puts "no".color(:red)
         try_fixing_it(
           sudo_gitlab("RAILS_ENV=production bin/mail_room start")
         )
@@ -730,9 +730,9 @@ namespace :gitlab do
       end
 
       if connected
-        puts "yes".green
+        puts "yes".color(:green)
       else
-        puts "no".red
+        puts "no".color(:red)
         try_fixing_it(
           "Check that the information in config/gitlab.yml is correct"
         )
@@ -800,7 +800,7 @@ namespace :gitlab do
   namespace :user do
     desc "GitLab | Check the integrity of a specific user's repositories"
     task :check_repos, [:username] => :environment do |t, args|
-      username = args[:username] || prompt("Check repository integrity for which username? ".blue)
+      username = args[:username] || prompt("Check repository integrity for which username? ".color(:blue))
       user = User.find_by(username: username)
       if user
         repo_dirs = user.authorized_projects.map do |p|
@@ -812,7 +812,7 @@ namespace :gitlab do
 
         repo_dirs.each { |repo_dir| check_repo_integrity(repo_dir) }
       else
-        puts "\nUser '#{username}' not found".red
+        puts "\nUser '#{username}' not found".color(:red)
       end
     end
   end
@@ -821,13 +821,13 @@ namespace :gitlab do
   ##########################
 
   def fix_and_rerun
-    puts "  Please #{"fix the error above"} and rerun the checks.".red
+    puts "  Please #{"fix the error above"} and rerun the checks.".color(:red)
   end
 
   def for_more_information(*sources)
     sources = sources.shift if sources.first.is_a?(Array)
 
-    puts "  For more information see:".blue
+    puts "  For more information see:".color(:blue)
     sources.each do |source|
       puts "  #{source}"
     end
@@ -835,7 +835,7 @@ namespace :gitlab do
 
   def finished_checking(component)
     puts ""
-    puts "Checking #{component.yellow} ... #{"Finished".green}"
+    puts "Checking #{component.color(:yellow)} ... #{"Finished".color(:green)}"
     puts ""
   end
 
@@ -856,14 +856,14 @@ namespace :gitlab do
   end
 
   def start_checking(component)
-    puts "Checking #{component.yellow} ..."
+    puts "Checking #{component.color(:yellow)} ..."
     puts ""
   end
 
   def try_fixing_it(*steps)
     steps = steps.shift if steps.first.is_a?(Array)
 
-    puts "  Try fixing it:".blue
+    puts "  Try fixing it:".color(:blue)
     steps.each do |step|
       puts "  #{step}"
     end
@@ -875,9 +875,9 @@ namespace :gitlab do
 
     print "GitLab Shell version >= #{required_version} ? ... "
     if current_version.valid? && required_version <= current_version
-      puts "OK (#{current_version})".green
+      puts "OK (#{current_version})".color(:green)
     else
-      puts "FAIL. Please update gitlab-shell to #{required_version} from #{current_version}".red
+      puts "FAIL. Please update gitlab-shell to #{required_version} from #{current_version}".color(:red)
     end
   end
 
@@ -888,9 +888,9 @@ namespace :gitlab do
     print "Ruby version >= #{required_version} ? ... "
 
     if current_version.valid? && required_version <= current_version
-      puts "yes (#{current_version})".green
+      puts "yes (#{current_version})".color(:green)
     else
-      puts "no".red
+      puts "no".color(:red)
       try_fixing_it(
         "Update your ruby to a version >= #{required_version} from #{current_version}"
       )
@@ -906,9 +906,9 @@ namespace :gitlab do
     print "Git version >= #{required_version} ? ... "
 
     if current_version.valid? && required_version <= current_version
-      puts "yes (#{current_version})".green
+      puts "yes (#{current_version})".color(:green)
     else
-      puts "no".red
+      puts "no".color(:red)
       try_fixing_it(
         "Update your git to a version >= #{required_version} from #{current_version}"
       )
@@ -926,9 +926,9 @@ namespace :gitlab do
 
   def sanitized_message(project)
     if should_sanitize?
-      "#{project.namespace_id.to_s.yellow}/#{project.id.to_s.yellow} ... "
+      "#{project.namespace_id.to_s.color(:yellow)}/#{project.id.to_s.color(:yellow)} ... "
     else
-      "#{project.name_with_namespace.yellow} ... "
+      "#{project.name_with_namespace.color(:yellow)} ... "
     end
   end
 
@@ -941,7 +941,7 @@ namespace :gitlab do
   end
 
   def check_repo_integrity(repo_dir)
-    puts "\nChecking repo at #{repo_dir.yellow}"
+    puts "\nChecking repo at #{repo_dir.color(:yellow)}"
 
     git_fsck(repo_dir)
     check_config_lock(repo_dir)
@@ -949,48 +949,48 @@ namespace :gitlab do
   end
 
   def git_fsck(repo_dir)
-    puts "Running `git fsck`".yellow
+    puts "Running `git fsck`".color(:yellow)
     system(*%W(#{Gitlab.config.git.bin_path} fsck), chdir: repo_dir)
   end
 
   def check_config_lock(repo_dir)
     config_exists = File.exist?(File.join(repo_dir,'config.lock'))
-    config_output = config_exists ? 'yes'.red : 'no'.green
-    puts "'config.lock' file exists?".yellow + " ... #{config_output}"
+    config_output = config_exists ? 'yes'.color(:red) : 'no'.color(:green)
+    puts "'config.lock' file exists?".color(:yellow) + " ... #{config_output}"
   end
 
   def check_ref_locks(repo_dir)
     lock_files = Dir.glob(File.join(repo_dir,'refs/heads/*.lock'))
     if lock_files.present?
-      puts "Ref lock files exist:".red
+      puts "Ref lock files exist:".color(:red)
       lock_files.each do |lock_file|
         puts "  #{lock_file}"
       end
     else
-      puts "No ref lock files exist".green
+      puts "No ref lock files exist".color(:green)
     end
   end
 
   def check_elasticsearch
-    client = Elasticsearch::Client.new(host: Gitlab.config.elasticsearch.host,
-                                     port: Gitlab.config.elasticsearch.port)
+    client = Elasticsearch::Client.new(host: ApplicationSetting.current.elasticsearch_host,
+                                       port: ApplicationSetting.current.elasticsearch_port)
 
     print "Elasticsearch version >= 2.0? ... "
 
     version = client.info["version"]["number"]
 
     if version.starts_with?("2")
-      puts "yes (#{version})".green
+      puts "yes (#{version})".color(:green)
     else
-      puts "no".red
+      puts "no".color(:red)
     end
 
     print "Elasticsearch has plugin delete-by-query installed? ... "
 
     if client.cat.plugins.include?("delete-by-query")
-      puts "yes".green
+      puts "yes".color(:green)
     else
-      puts "no".red
+      puts "no".color(:red)
     end
   end
 end
diff --git a/lib/tasks/gitlab/cleanup.rake b/lib/tasks/gitlab/cleanup.rake
index 9f5852ac6133b967180f8efb36cb64eb956f2572..ab0028d6603013ad73ef92fdf97ac643282e6fdc 100644
--- a/lib/tasks/gitlab/cleanup.rake
+++ b/lib/tasks/gitlab/cleanup.rake
@@ -10,7 +10,7 @@ namespace :gitlab do
       git_base_path = Gitlab.config.gitlab_shell.repos_path
       all_dirs = Dir.glob(git_base_path + '/*')
 
-      puts git_base_path.yellow
+      puts git_base_path.color(:yellow)
       puts "Looking for directories to remove... "
 
       all_dirs.reject! do |dir|
@@ -29,17 +29,17 @@ namespace :gitlab do
 
         if remove_flag
           if FileUtils.rm_rf dir_path
-            puts "Removed...#{dir_path}".red
+            puts "Removed...#{dir_path}".color(:red)
           else
-            puts "Cannot remove #{dir_path}".red
+            puts "Cannot remove #{dir_path}".color(:red)
           end
         else
-          puts "Can be removed: #{dir_path}".red
+          puts "Can be removed: #{dir_path}".color(:red)
         end
       end
 
       unless remove_flag
-        puts "To cleanup this directories run this command with REMOVE=true".yellow
+        puts "To cleanup this directories run this command with REMOVE=true".color(:yellow)
       end
     end
 
@@ -75,19 +75,19 @@ namespace :gitlab do
         next unless user.ldap_user?
         print "#{user.name} (#{user.ldap_identity.extern_uid}) ..."
         if Gitlab::LDAP::Access.allowed?(user)
-          puts " [OK]".green
+          puts " [OK]".color(:green)
         else
           if block_flag
             user.block! unless user.blocked?
-            puts " [BLOCKED]".red
+            puts " [BLOCKED]".color(:red)
           else
-            puts " [NOT IN LDAP]".yellow
+            puts " [NOT IN LDAP]".color(:yellow)
           end
         end
       end
 
       unless block_flag
-        puts "To block these users run this command with BLOCK=true".yellow
+        puts "To block these users run this command with BLOCK=true".color(:yellow)
       end
     end
   end
diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake
index e473b7560234f0b626c5699b4ffe720379632bf7..7230b9485bea741e5d64c6429321c680adb12fbd 100644
--- a/lib/tasks/gitlab/db.rake
+++ b/lib/tasks/gitlab/db.rake
@@ -3,22 +3,22 @@ namespace :gitlab do
     desc 'GitLab | Manually insert schema migration version'
     task :mark_migration_complete, [:version] => :environment do |_, args|
       unless args[:version]
-        puts "Must specify a migration version as an argument".red
+        puts "Must specify a migration version as an argument".color(:red)
         exit 1
       end
 
       version = args[:version].to_i
       if version == 0
-        puts "Version '#{args[:version]}' must be a non-zero integer".red
+        puts "Version '#{args[:version]}' must be a non-zero integer".color(:red)
         exit 1
       end
 
       sql = "INSERT INTO schema_migrations (version) VALUES (#{version})"
       begin
         ActiveRecord::Base.connection.execute(sql)
-        puts "Successfully marked '#{version}' as complete".green
+        puts "Successfully marked '#{version}' as complete".color(:green)
       rescue ActiveRecord::RecordNotUnique
-        puts "Migration version '#{version}' is already marked complete".yellow
+        puts "Migration version '#{version}' is already marked complete".color(:yellow)
       end
     end
 
@@ -34,7 +34,17 @@ namespace :gitlab do
       # PG: http://www.postgresql.org/docs/current/static/ddl-depend.html
       # MySQL: http://dev.mysql.com/doc/refman/5.7/en/drop-table.html
       # Add `IF EXISTS` because cascade could have already deleted a table.
-      tables.each { |t| connection.execute("DROP TABLE IF EXISTS #{t} CASCADE") }
+      tables.each { |t| connection.execute("DROP TABLE IF EXISTS #{connection.quote_table_name(t)} CASCADE") }
+    end
+
+    desc 'Configures the database by running migrate, or by loading the schema and seeding if needed'
+    task configure: :environment do
+      if ActiveRecord::Base.connection.tables.any?
+        Rake::Task['db:migrate'].invoke
+      else
+        Rake::Task['db:schema:load'].invoke
+        Rake::Task['db:seed_fu'].invoke
+      end
     end
   end
 end
diff --git a/lib/tasks/gitlab/elastic.rake b/lib/tasks/gitlab/elastic.rake
index ef613055da14aabca031e7cbc8ca7f178e858d23..8a98584f41747decbcaf2de58ef1328bfabf67ac 100644
--- a/lib/tasks/gitlab/elastic.rake
+++ b/lib/tasks/gitlab/elastic.rake
@@ -35,7 +35,7 @@ namespace :gitlab do
             head_commit = repository.commit
 
             if !head_commit || index_status.last_commit == head_commit.sha
-              puts "Skipped".yellow
+              puts "Skipped".color(:yellow)
               next
             end
 
@@ -48,7 +48,7 @@ namespace :gitlab do
             # During indexing the new commits can be pushed,
             # the last_commit parameter only indicates that at least this commit is in index
             index_status.update(last_commit: head_commit.sha, indexed_at: DateTime.now)
-            puts "Done!".green
+            puts "Done!".color(:green)
           rescue StandardError => e
             puts "#{e.message}, trace - #{e.backtrace}"
           end
@@ -67,7 +67,7 @@ namespace :gitlab do
           puts "Indexing wiki of #{project.name_with_namespace}..."
           begin
             project.wiki.index_blobs
-            puts "Done!".green
+            puts "Done!".color(:green)
           rescue StandardError => e
             puts "#{e.message}, trace - #{e.backtrace}"
           end
@@ -88,7 +88,7 @@ namespace :gitlab do
           klass.import
         end
 
-        puts "done".green
+        puts "done".color(:green)
       end
     end
 
@@ -111,7 +111,7 @@ namespace :gitlab do
         klass.import
       end
 
-      puts "done".green
+      puts "done".color(:green)
     end
 
     desc "GitLab | Create empty Elasticsearch indexes"
@@ -130,14 +130,14 @@ namespace :gitlab do
 
         klass.__elasticsearch__.create_index!
 
-        puts "done".green
+        puts "done".color(:green)
       end
     end
 
     desc "GitLab | Clear Elasticsearch indexing status"
     task clear_index_status: :environment do
       IndexStatus.destroy_all
-      puts "Done".green
+      puts "Done".color(:green)
     end
 
     desc "GitLab | Delete Elasticsearch indexes"
@@ -156,7 +156,7 @@ namespace :gitlab do
 
         klass.__elasticsearch__.delete_index!
 
-        puts "done".green
+        puts "done".color(:green)
       end
     end
 
diff --git a/lib/tasks/gitlab/git.rake b/lib/tasks/gitlab/git.rake
index 65ee430d550ff54c144cfd1d0532f5b74c689e64..f9834a4dae8441f21f92ade58cdf4d32085e8b28 100644
--- a/lib/tasks/gitlab/git.rake
+++ b/lib/tasks/gitlab/git.rake
@@ -5,7 +5,7 @@ namespace :gitlab do
     task repack: :environment do
       failures = perform_git_cmd(%W(git repack -a --quiet), "Repacking repo")
       if failures.empty?
-        puts "Done".green
+        puts "Done".color(:green)
       else
         output_failures(failures)
       end
@@ -15,7 +15,7 @@ namespace :gitlab do
     task gc: :environment do
       failures = perform_git_cmd(%W(git gc --auto --quiet), "Garbage Collecting")
       if failures.empty?
-        puts "Done".green
+        puts "Done".color(:green)
       else
         output_failures(failures)
       end
@@ -25,7 +25,7 @@ namespace :gitlab do
     task prune: :environment do
       failures = perform_git_cmd(%W(git prune), "Git Prune")
       if failures.empty?
-        puts "Done".green
+        puts "Done".color(:green)
       else
         output_failures(failures)
       end
@@ -47,7 +47,7 @@ namespace :gitlab do
     end
 
     def output_failures(failures)
-      puts "The following repositories reported errors:".red
+      puts "The following repositories reported errors:".color(:red)
       failures.each { |f| puts "- #{f}" }
     end
 
diff --git a/lib/tasks/gitlab/import.rake b/lib/tasks/gitlab/import.rake
index 1c04f47f08ffe2b5e8239b0218e4d148b2740711..4753f00c26ab76512a3033c939e04e6e2a49bb8b 100644
--- a/lib/tasks/gitlab/import.rake
+++ b/lib/tasks/gitlab/import.rake
@@ -23,7 +23,7 @@ namespace :gitlab do
         group_name, name = File.split(path)
         group_name = nil if group_name == '.'
 
-        puts "Processing #{repo_path}".yellow
+        puts "Processing #{repo_path}".color(:yellow)
 
         if path.end_with?('.wiki')
           puts " * Skipping wiki repo"
@@ -51,9 +51,9 @@ namespace :gitlab do
               group.path = group_name
               group.owner = user
               if group.save
-                puts " * Created Group #{group.name} (#{group.id})".green
+                puts " * Created Group #{group.name} (#{group.id})".color(:green)
               else
-                puts " * Failed trying to create group #{group.name}".red
+                puts " * Failed trying to create group #{group.name}".color(:red)
               end
             end
             # set project group
@@ -63,17 +63,17 @@ namespace :gitlab do
           project = Projects::CreateService.new(user, project_params).execute
 
           if project.persisted?
-            puts " * Created #{project.name} (#{repo_path})".green
+            puts " * Created #{project.name} (#{repo_path})".color(:green)
             project.update_repository_size
             project.update_commit_count
           else
-            puts " * Failed trying to create #{project.name} (#{repo_path})".red
-            puts "   Errors: #{project.errors.messages}".red
+            puts " * Failed trying to create #{project.name} (#{repo_path})".color(:red)
+            puts "   Errors: #{project.errors.messages}".color(:red)
           end
         end
       end
 
-      puts "Done!".green
+      puts "Done!".color(:green)
     end
   end
 end
diff --git a/lib/tasks/gitlab/info.rake b/lib/tasks/gitlab/info.rake
index d6883a563eee8cc92dcafa351f2e7df36a701a4c..a52ee3be7355cb1d38c257e45a65573616e723b1 100644
--- a/lib/tasks/gitlab/info.rake
+++ b/lib/tasks/gitlab/info.rake
@@ -15,20 +15,21 @@ namespace :gitlab do
       rake_version = run_and_match(%W(rake --version), /[\d\.]+/).try(:to_s)
 
       puts ""
-      puts "System information".yellow
-      puts "System:\t\t#{os_name || "unknown".red}"
+      puts "System information".color(:yellow)
+      puts "System:\t\t#{os_name || "unknown".color(:red)}"
       puts "Current User:\t#{run(%W(whoami))}"
-      puts "Using RVM:\t#{rvm_version.present? ? "yes".green : "no"}"
+      puts "Using RVM:\t#{rvm_version.present? ? "yes".color(:green) : "no"}"
       puts "RVM Version:\t#{rvm_version}" if rvm_version.present?
-      puts "Ruby Version:\t#{ruby_version || "unknown".red}"
-      puts "Gem Version:\t#{gem_version || "unknown".red}"
-      puts "Bundler Version:#{bunder_version || "unknown".red}"
-      puts "Rake Version:\t#{rake_version || "unknown".red}"
+      puts "Ruby Version:\t#{ruby_version || "unknown".color(:red)}"
+      puts "Gem Version:\t#{gem_version || "unknown".color(:red)}"
+      puts "Bundler Version:#{bunder_version || "unknown".color(:red)}"
+      puts "Rake Version:\t#{rake_version || "unknown".color(:red)}"
       puts "Sidekiq Version:#{Sidekiq::VERSION}"
 
 
       # check database adapter
-      database_adapter = ActiveRecord::Base.connection.adapter_name.downcase
+      database_adapter = Gitlab::Database.adapter_name
+      database_version = Gitlab::Database.version
 
       project = Group.new(path: "some-group").projects.build(path: "some-project")
       # construct clone URLs
@@ -39,17 +40,21 @@ namespace :gitlab do
       omniauth_providers.map! { |provider| provider['name'] }
 
       puts ""
-      puts "GitLab information".yellow
+      puts "GitLab information".color(:yellow)
       puts "Version:\t#{Gitlab::VERSION}"
       puts "Revision:\t#{Gitlab::REVISION}"
       puts "Directory:\t#{Rails.root}"
       puts "DB Adapter:\t#{database_adapter}"
+      puts "DB Version:\t#{database_version}"
       puts "URL:\t\t#{Gitlab.config.gitlab.url}"
       puts "HTTP Clone URL:\t#{http_clone_url}"
       puts "SSH Clone URL:\t#{ssh_clone_url}"
-      puts "Using LDAP:\t#{Gitlab.config.ldap.enabled ? "yes".green : "no"}"
-      puts "Using Omniauth:\t#{Gitlab.config.omniauth.enabled ? "yes".green : "no"}"
-      puts "Omniauth Providers: #{omniauth_providers.map(&:magenta).join(', ')}" if Gitlab.config.omniauth.enabled
+      puts "Elasticsearch:\t#{Gitlab.config.elasticsearch.enabled ? "yes".color(:green) : "no"}"
+      puts "Geo:\t\t#{Gitlab::Geo.enabled? ? "yes".color(:green) : "no"}"
+      puts "Geo node:\t#{Gitlab::Geo.current_node.primary ? "Primary" : "Secondary"}" if Gitlab::Geo.enabled?
+      puts "Using LDAP:\t#{Gitlab.config.ldap.enabled ? "yes".color(:green) : "no"}"
+      puts "Using Omniauth:\t#{Gitlab.config.omniauth.enabled ? "yes".color(:green) : "no"}"
+      puts "Omniauth Providers: #{omniauth_providers.join(', ')}" if Gitlab.config.omniauth.enabled
 
 
 
@@ -60,8 +65,8 @@ namespace :gitlab do
       end
 
       puts ""
-      puts "GitLab Shell".yellow
-      puts "Version:\t#{gitlab_shell_version || "unknown".red}"
+      puts "GitLab Shell".color(:yellow)
+      puts "Version:\t#{gitlab_shell_version || "unknown".color(:red)}"
       puts "Repositories:\t#{Gitlab.config.gitlab_shell.repos_path}"
       puts "Hooks:\t\t#{Gitlab.config.gitlab_shell.hooks_path}"
       puts "Git:\t\t#{Gitlab.config.git.bin_path}"
diff --git a/lib/tasks/gitlab/setup.rake b/lib/tasks/gitlab/setup.rake
index 48baecfd2a23614aeb192ab833088ce6a27f9fcb..05fcb8e3da5806ed8c3208a81d0085fb29358aac 100644
--- a/lib/tasks/gitlab/setup.rake
+++ b/lib/tasks/gitlab/setup.rake
@@ -19,7 +19,7 @@ namespace :gitlab do
     Rake::Task["setup_postgresql"].invoke
     Rake::Task["db:seed_fu"].invoke
   rescue Gitlab::TaskAbortedByUserError
-    puts "Quitting...".red
+    puts "Quitting...".color(:red)
     exit 1
   end
 end
diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake
index dd61632e5573b3229c0666c0ff904a778553c2cd..b1648a4602abab48320fdb1a0350017b722df1e9 100644
--- a/lib/tasks/gitlab/shell.rake
+++ b/lib/tasks/gitlab/shell.rake
@@ -118,12 +118,12 @@ namespace :gitlab do
     puts ""
 
     unless $?.success?
-      puts "Failed to add keys...".red
+      puts "Failed to add keys...".color(:red)
       exit 1
     end
 
   rescue Gitlab::TaskAbortedByUserError
-    puts "Quitting...".red
+    puts "Quitting...".color(:red)
     exit 1
   end
 
diff --git a/lib/tasks/gitlab/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake
index d33b5b31e189af40ab2c125d55ee919c6c473df8..d0c019044b7eed695b8a82d2a810a799894e492f 100644
--- a/lib/tasks/gitlab/task_helpers.rake
+++ b/lib/tasks/gitlab/task_helpers.rake
@@ -2,7 +2,7 @@ module Gitlab
   class TaskAbortedByUserError < StandardError; end
 end
 
-String.disable_colorization = true unless STDOUT.isatty
+require 'rainbow/ext/string'
 
 # Prevent StateMachine warnings from outputting during a cron task
 StateMachines::Machine.ignore_method_conflicts = true if ENV['CRON']
@@ -14,7 +14,7 @@ namespace :gitlab do
   # Returns "yes" the user chose to continue
   # Raises Gitlab::TaskAbortedByUserError if the user chose *not* to continue
   def ask_to_continue
-    answer = prompt("Do you want to continue (yes/no)? ".blue, %w{yes no})
+    answer = prompt("Do you want to continue (yes/no)? ".color(:blue), %w{yes no})
     raise Gitlab::TaskAbortedByUserError unless answer == "yes"
   end
 
@@ -98,10 +98,10 @@ namespace :gitlab do
       gitlab_user = Gitlab.config.gitlab.user
       current_user = run(%W(whoami)).chomp
       unless current_user == gitlab_user
-        puts " Warning ".colorize(:black).on_yellow
-        puts "  You are running as user #{current_user.magenta}, we hope you know what you are doing."
+        puts " Warning ".color(:black).background(:yellow)
+        puts "  You are running as user #{current_user.color(:magenta)}, we hope you know what you are doing."
         puts "  Things may work\/fail for the wrong reasons."
-        puts "  For correct results you should run this as user #{gitlab_user.magenta}."
+        puts "  For correct results you should run this as user #{gitlab_user.color(:magenta)}."
         puts ""
       end
       @warned_user_not_gitlab = true
diff --git a/lib/tasks/gitlab/two_factor.rake b/lib/tasks/gitlab/two_factor.rake
index 9196677a01769d19df4fb4858f84850312fb0589..fc0ccc726ed0eae3e13f35b434739c82ed1e0a21 100644
--- a/lib/tasks/gitlab/two_factor.rake
+++ b/lib/tasks/gitlab/two_factor.rake
@@ -6,17 +6,17 @@ namespace :gitlab do
       count = scope.count
 
       if count > 0
-        puts "This will disable 2FA for #{count.to_s.red} users..."
+        puts "This will disable 2FA for #{count.to_s.color(:red)} users..."
 
         begin
           ask_to_continue
           scope.find_each(&:disable_two_factor!)
-          puts "Successfully disabled 2FA for #{count} users.".green
+          puts "Successfully disabled 2FA for #{count} users.".color(:green)
         rescue Gitlab::TaskAbortedByUserError
-          puts "Quitting...".red
+          puts "Quitting...".color(:red)
         end
       else
-        puts "There are currently no users with 2FA enabled.".yellow
+        puts "There are currently no users with 2FA enabled.".color(:yellow)
       end
     end
   end
diff --git a/lib/tasks/gitlab/update_commit_count.rake b/lib/tasks/gitlab/update_commit_count.rake
index 9b636f12d9f06a9cd9e1bb2256823097b351861d..3bd10b0208bf7fa78cc40c784d871715c8767047 100644
--- a/lib/tasks/gitlab/update_commit_count.rake
+++ b/lib/tasks/gitlab/update_commit_count.rake
@@ -6,15 +6,15 @@ namespace :gitlab do
     ask_to_continue unless ENV['force'] == 'yes'
 
     projects.find_each(batch_size: 100) do |project|
-      print "#{project.name_with_namespace.yellow} ... "
+      print "#{project.name_with_namespace.color(:yellow)} ... "
 
       unless project.repo_exists?
-        puts "skipping, because the repo is empty".magenta
+        puts "skipping, because the repo is empty".color(:magenta)
         next
       end
 
       project.update_commit_count
-      puts project.commit_count.to_s.green
+      puts project.commit_count.to_s.color(:green)
     end
   end
 end
diff --git a/lib/tasks/gitlab/update_gitignore.rake b/lib/tasks/gitlab/update_gitignore.rake
new file mode 100644
index 0000000000000000000000000000000000000000..4fd48cccb1d4606ef8c2124fb866d11dd4206bdb
--- /dev/null
+++ b/lib/tasks/gitlab/update_gitignore.rake
@@ -0,0 +1,46 @@
+namespace :gitlab do
+  desc "GitLab | Update gitignore"
+  task :update_gitignore do
+    unless clone_gitignores
+      puts "Cloning the gitignores failed".color(:red)
+      return
+    end
+
+    remove_unneeded_files(gitignore_directory)
+    remove_unneeded_files(global_directory)
+
+    puts "Done".color(:green)
+  end
+
+  def clone_gitignores
+    FileUtils.rm_rf(gitignore_directory) if Dir.exist?(gitignore_directory)
+    FileUtils.cd vendor_directory
+
+    system('git clone --depth=1 --branch=master https://github.com/github/gitignore.git')
+  end
+
+  # Retain only certain files:
+  # - The LICENSE, because we have to
+  # - The sub dir global
+  # - The gitignores themself
+  # - Dir.entires returns also the entries '.' and '..'
+  def remove_unneeded_files(path)
+    Dir.foreach(path) do |file|
+      FileUtils.rm_rf(File.join(path, file)) unless file =~ /(\.{1,2}|LICENSE|Global|\.gitignore)\z/
+    end
+  end
+
+  private
+
+  def vendor_directory
+    Rails.root.join('vendor')
+  end
+
+  def gitignore_directory
+    File.join(vendor_directory, 'gitignore')
+  end
+
+  def global_directory
+    File.join(gitignore_directory, 'Global')
+  end
+end
diff --git a/lib/tasks/gitlab/web_hook.rake b/lib/tasks/gitlab/web_hook.rake
index cc0f668474eda09b889908580466d14e86fbb59e..f467cc0ee29f53419b40f8ebe334fb4abbcc045d 100644
--- a/lib/tasks/gitlab/web_hook.rake
+++ b/lib/tasks/gitlab/web_hook.rake
@@ -12,9 +12,9 @@ namespace :gitlab do
         print "- #{project.name} ... "
         web_hook = project.hooks.new(url: web_hook_url)
         if web_hook.save
-          puts "added".green
+          puts "added".color(:green)
         else
-          print "failed".red
+          print "failed".color(:red)
           puts "  [#{web_hook.errors.full_messages.to_sentence}]"
         end
       end
@@ -57,7 +57,7 @@ namespace :gitlab do
       if namespace
         Project.in_namespace(namespace.id)
       else
-        puts "Namespace not found: #{namespace_path}".red
+        puts "Namespace not found: #{namespace_path}".color(:red)
         exit 2
       end
     end
diff --git a/lib/tasks/migrate/migrate_iids.rake b/lib/tasks/migrate/migrate_iids.rake
index d258c6fd08dc4dc33d07b9aede6972c39a76855f..4f2486157b74da0927c4eb2aa7c14de8fe6bfc45 100644
--- a/lib/tasks/migrate/migrate_iids.rake
+++ b/lib/tasks/migrate/migrate_iids.rake
@@ -1,6 +1,6 @@
 desc "GitLab | Build internal ids for issues and merge requests"
 task migrate_iids: :environment do
-  puts 'Issues'.yellow
+  puts 'Issues'.color(:yellow)
   Issue.where(iid: nil).find_each(batch_size: 100) do |issue|
     begin
       issue.set_iid
@@ -15,7 +15,7 @@ task migrate_iids: :environment do
   end
 
   puts 'done'
-  puts 'Merge Requests'.yellow
+  puts 'Merge Requests'.color(:yellow)
   MergeRequest.where(iid: nil).find_each(batch_size: 100) do |mr|
     begin
       mr.set_iid
@@ -30,7 +30,7 @@ task migrate_iids: :environment do
   end
 
   puts 'done'
-  puts 'Milestones'.yellow
+  puts 'Milestones'.color(:yellow)
   Milestone.where(iid: nil).find_each(batch_size: 100) do |m|
     begin
       m.set_iid
diff --git a/lib/tasks/rubocop.rake b/lib/tasks/rubocop.rake
index ddfaf5d51f281225a717c4fe8ef4e8ce4dc1a0e7..78ffccc9d0658678c6eddc3f5dd60c3c43543219 100644
--- a/lib/tasks/rubocop.rake
+++ b/lib/tasks/rubocop.rake
@@ -1,4 +1,5 @@
 unless Rails.env.production?
   require 'rubocop/rake_task'
+
   RuboCop::RakeTask.new
 end
diff --git a/lib/tasks/spinach.rake b/lib/tasks/spinach.rake
index 01d23b89bb7e5fe892688d73aec17dff8436573b..da255f5464b04f77be7b1cca3d95904f9102b989 100644
--- a/lib/tasks/spinach.rake
+++ b/lib/tasks/spinach.rake
@@ -52,7 +52,7 @@ def run_spinach_tests(tags)
 
     tests = File.foreach('tmp/spinach-rerun.txt').map(&:chomp)
     puts ''
-    puts "Spinach tests for #{tags}: Retrying tests... #{tests}".red
+    puts "Spinach tests for #{tags}: Retrying tests... #{tests}".color(:red)
     puts ''
     sleep(3)
     success = run_spinach_command(tests)
diff --git a/scripts/merge-reports b/scripts/merge-reports
new file mode 100755
index 0000000000000000000000000000000000000000..f7b574001acb1712c32a00dd50454e5bf0069818
--- /dev/null
+++ b/scripts/merge-reports
@@ -0,0 +1,29 @@
+#!/usr/bin/env ruby
+
+require 'json'
+require 'yaml'
+
+main_report_file = ARGV.shift
+unless main_report_file
+  puts 'usage: merge_reports <main-report> [extra reports...]'
+  exit 1
+end
+
+puts "Loading #{main_report_file}..."
+main_report = JSON.parse(File.read(main_report_file))
+new_report = main_report.dup
+
+ARGV.each do |report_file|
+  report = JSON.parse(File.read(report_file))
+
+  # Remove existing values
+  updates = report.delete_if do |key, value|
+    main_report[key] && main_report[key] == value
+  end
+  new_report.merge!(updates)
+
+  puts "Merged #{report_file} adding #{updates.size} results."
+end
+
+File.write(main_report_file, JSON.pretty_generate(new_report))
+puts "Saved #{main_report_file}."
diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh
index 247383aa46c6423486d9f864b6908a24129b27e1..7e71a0309014943b24ee986ac9a2785d72fb0a5e 100755
--- a/scripts/prepare_build.sh
+++ b/scripts/prepare_build.sh
@@ -1,21 +1,25 @@
 #!/bin/bash
 
 retry() {
-    for i in $(seq 1 3); do
+    if eval "$@"; then
+        return 0
+    fi
+
+    for i in 2 1; do
+        sleep 3s
+        echo "Retrying $i..."
         if eval "$@"; then
             return 0
         fi
-        sleep 3s
-        echo "Retrying..."
     done
     return 1
 }
 
 if [ -f /.dockerenv ] || [ -f ./dockerinit ]; then
-    mkdir -p vendor
+    mkdir -p vendor/apt
 
     # Install phantomjs package
-    pushd vendor
+    pushd vendor/apt
     if [ ! -e phantomjs_1.9.8-0jessie_amd64.deb ]; then
         wget -q https://gitlab.com/axil/phantomjs-debian/raw/master/phantomjs_1.9.8-0jessie_amd64.deb
     fi
diff --git a/spec/controllers/admin/projects_controller_spec.rb b/spec/controllers/admin/projects_controller_spec.rb
index 2ba0d4891978de11bdc87b3595764818b8d6908e..4cb8b8da15019a98d778af0d462a2db9750f44b2 100644
--- a/spec/controllers/admin/projects_controller_spec.rb
+++ b/spec/controllers/admin/projects_controller_spec.rb
@@ -17,7 +17,7 @@ describe Admin::ProjectsController do
 
     it 'does not retrieve the project' do
       get :index, visibility_levels: [Gitlab::VisibilityLevel::INTERNAL]
-      expect(response.body).to_not match(project.name)
+      expect(response.body).not_to match(project.name)
     end
   end
 end
diff --git a/spec/controllers/blob_controller_spec.rb b/spec/controllers/blob_controller_spec.rb
index eb91e577b875ccb3d917f705579e487d7ad2220f..465013231f9de7b3ca1596601f2f7ce13bb1ddbd 100644
--- a/spec/controllers/blob_controller_spec.rb
+++ b/spec/controllers/blob_controller_spec.rb
@@ -38,6 +38,11 @@ describe Projects::BlobController do
       let(:id) { 'invalid-branch/README.md' }
       it { is_expected.to respond_with(:not_found) }
     end
+
+    context "binary file" do
+      let(:id) { 'binary-encoding/encoding/binary-1.bin' }
+      it { is_expected.to respond_with(:success) }
+    end
   end
 
   describe 'GET show with tree path' do
diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb
index a59865987151f57d85e0f12194a65c79ced1ab86..89c2c26a367630f87304f59d4d42a9290be5ec89 100644
--- a/spec/controllers/groups/group_members_controller_spec.rb
+++ b/spec/controllers/groups/group_members_controller_spec.rb
@@ -4,17 +4,211 @@ describe Groups::GroupMembersController do
   let(:user)  { create(:user) }
   let(:group) { create(:group) }
 
-  context "index" do
+  describe '#index' do
     before do
       group.add_owner(user)
       stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
     end
 
     it 'renders index with group members' do
-      get :index, group_id: group.path
+      get :index, group_id: group
 
       expect(response.status).to eq(200)
       expect(response).to render_template(:index)
     end
   end
+
+  describe '#destroy' do
+    let(:group) { create(:group, :public) }
+
+    context 'when member is not found' do
+      it 'returns 403' do
+        delete :destroy, group_id: group,
+                         id: 42
+
+        expect(response.status).to eq(403)
+      end
+    end
+
+    context 'when member is found' do
+      let(:user) { create(:user) }
+      let(:group_user) { create(:user) }
+      let(:member) do
+        group.add_developer(group_user)
+        group.members.find_by(user_id: group_user)
+      end
+
+      context 'when user does not have enough rights' do
+        before do
+          group.add_developer(user)
+          sign_in(user)
+        end
+
+        it 'returns 403' do
+          delete :destroy, group_id: group,
+                           id: member
+
+          expect(response.status).to eq(403)
+          expect(group.users).to include group_user
+        end
+      end
+
+      context 'when user has enough rights' do
+        before do
+          group.add_owner(user)
+          sign_in(user)
+        end
+
+        it '[HTML] removes user from members' do
+          delete :destroy, group_id: group,
+                           id: member
+
+          expect(response).to set_flash.to 'User was successfully removed from group.'
+          expect(response).to redirect_to(group_group_members_path(group))
+          expect(group.users).not_to include group_user
+        end
+
+        it '[JS] removes user from members' do
+          xhr :delete, :destroy, group_id: group,
+                                 id: member
+
+          expect(response).to be_success
+          expect(group.users).not_to include group_user
+        end
+      end
+    end
+  end
+
+  describe '#leave' do
+    let(:group) { create(:group, :public) }
+    let(:user) { create(:user) }
+
+    context 'when member is not found' do
+      before { sign_in(user) }
+
+      it 'returns 403' do
+        delete :leave, group_id: group
+
+        expect(response.status).to eq(403)
+      end
+    end
+
+    context 'when member is found' do
+      context 'and is not an owner' do
+        before do
+          group.add_developer(user)
+          sign_in(user)
+        end
+
+        it 'removes user from members' do
+          delete :leave, group_id: group
+
+          expect(response).to set_flash.to "You left the \"#{group.name}\" group."
+          expect(response).to redirect_to(dashboard_groups_path)
+          expect(group.users).not_to include user
+        end
+      end
+
+      context 'and is an owner' do
+        before do
+          group.add_owner(user)
+          sign_in(user)
+        end
+
+        it 'cannot removes himself from the group' do
+          delete :leave, group_id: group
+
+          expect(response).to redirect_to(group_path(group))
+          expect(response).to set_flash[:alert].to "You can not leave the \"#{group.name}\" group. Transfer or delete the group."
+          expect(group.users).to include user
+        end
+      end
+
+      context 'and is a requester' do
+        before do
+          group.request_access(user)
+          sign_in(user)
+        end
+
+        it 'removes user from members' do
+          delete :leave, group_id: group
+
+          expect(response).to set_flash.to 'Your access request to the group has been withdrawn.'
+          expect(response).to redirect_to(dashboard_groups_path)
+          expect(group.members.request).to be_empty
+          expect(group.users).not_to include user
+        end
+      end
+    end
+  end
+
+  describe '#request_access' do
+    let(:group) { create(:group, :public) }
+    let(:user) { create(:user) }
+
+    before do
+      sign_in(user)
+    end
+
+    it 'creates a new GroupMember that is not a team member' do
+      post :request_access, group_id: group
+
+      expect(response).to set_flash.to 'Your request for access has been queued for review.'
+      expect(response).to redirect_to(group_path(group))
+      expect(group.members.request.exists?(user_id: user)).to be_truthy
+      expect(group.users).not_to include user
+    end
+  end
+
+  describe '#approve_access_request' do
+    let(:group) { create(:group, :public) }
+
+    context 'when member is not found' do
+      it 'returns 403' do
+        post :approve_access_request, group_id: group,
+                                      id: 42
+
+        expect(response.status).to eq(403)
+      end
+    end
+
+    context 'when member is found' do
+      let(:user) { create(:user) }
+      let(:group_requester) { create(:user) }
+      let(:member) do
+        group.request_access(group_requester)
+        group.members.request.find_by(user_id: group_requester)
+      end
+
+      context 'when user does not have enough rights' do
+        before do
+          group.add_developer(user)
+          sign_in(user)
+        end
+
+        it 'returns 403' do
+          post :approve_access_request, group_id: group,
+                                        id: member
+
+          expect(response.status).to eq(403)
+          expect(group.users).not_to include group_requester
+        end
+      end
+
+      context 'when user has enough rights' do
+        before do
+          group.add_owner(user)
+          sign_in(user)
+        end
+
+        it 'adds user to members' do
+          post :approve_access_request, group_id: group,
+                                        id: member
+
+          expect(response).to redirect_to(group_group_members_path(group))
+          expect(group.users).to include group_requester
+        end
+      end
+    end
+  end
 end
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index 465531b2b367b45c1703027436ca6e6e002f0587..cd98fecd0c7fdc8bd3c6b54741daed81bfa5c987 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -31,9 +31,9 @@ describe GroupsController do
     let(:issue_2) { create(:issue, project: project) }
 
     before do
-      create_list(:upvote_note, 3, project: project, noteable: issue_2)
-      create_list(:upvote_note, 2, project: project, noteable: issue_1)
-      create_list(:downvote_note, 2, project: project, noteable: issue_2)
+      create_list(:award_emoji, 3, awardable: issue_2)
+      create_list(:award_emoji, 2, awardable: issue_1)
+      create_list(:award_emoji, 2, :downvote, awardable: issue_2,)
 
       sign_in(user)
     end
@@ -56,9 +56,9 @@ describe GroupsController do
     let(:merge_request_2) { create(:merge_request, :simple, source_project: project) }
 
     before do
-      create_list(:upvote_note, 3, project: project, noteable: merge_request_2)
-      create_list(:upvote_note, 2, project: project, noteable: merge_request_1)
-      create_list(:downvote_note, 2, project: project, noteable: merge_request_2)
+      create_list(:award_emoji, 3, awardable: merge_request_2)
+      create_list(:award_emoji, 2, awardable: merge_request_1)
+      create_list(:award_emoji, 2, :downvote, awardable: merge_request_2)
 
       sign_in(user)
     end
diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb
index 81c03c9059b5c08fafbafd1204ae030a285cf2e2..07bf8d2d1c39c6bcc69c05fbcaa1abe83dc4afe5 100644
--- a/spec/controllers/import/bitbucket_controller_spec.rb
+++ b/spec/controllers/import/bitbucket_controller_spec.rb
@@ -1,5 +1,4 @@
 require 'spec_helper'
-require_relative 'import_spec_helper'
 
 describe Import::BitbucketController do
   include ImportSpecHelper
diff --git a/spec/controllers/import/fogbugz_controller_spec.rb b/spec/controllers/import/fogbugz_controller_spec.rb
index 27b11267d2af3c6347b3f0a30a1a2c03ec28eff2..5f0f6dea82107ef42aec875c79fae55801284f6b 100644
--- a/spec/controllers/import/fogbugz_controller_spec.rb
+++ b/spec/controllers/import/fogbugz_controller_spec.rb
@@ -1,5 +1,4 @@
 require 'spec_helper'
-require_relative 'import_spec_helper'
 
 describe Import::FogbugzController do
   include ImportSpecHelper
diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb
index bcc713dce2ab25c84335e6514ae87755b95fb76f..c55a3c28208f7c66a5bb628597f9a921e2d6c112 100644
--- a/spec/controllers/import/github_controller_spec.rb
+++ b/spec/controllers/import/github_controller_spec.rb
@@ -1,5 +1,4 @@
 require 'spec_helper'
-require_relative 'import_spec_helper'
 
 describe Import::GithubController do
   include ImportSpecHelper
diff --git a/spec/controllers/import/gitlab_controller_spec.rb b/spec/controllers/import/gitlab_controller_spec.rb
index 198d006af76ccec6125d283a08f0f129a739d0e9..e8cf6aa7767210577adc7d9128478cbfddf32010 100644
--- a/spec/controllers/import/gitlab_controller_spec.rb
+++ b/spec/controllers/import/gitlab_controller_spec.rb
@@ -1,5 +1,4 @@
 require 'spec_helper'
-require_relative 'import_spec_helper'
 
 describe Import::GitlabController do
   include ImportSpecHelper
diff --git a/spec/controllers/import/gitorious_controller_spec.rb b/spec/controllers/import/gitorious_controller_spec.rb
index 7cb1b85a46d666a7712f73bc5dc71cf3c433191a..4ae2b78e11cb1e6befedba1f0b908dd49b835f29 100644
--- a/spec/controllers/import/gitorious_controller_spec.rb
+++ b/spec/controllers/import/gitorious_controller_spec.rb
@@ -1,5 +1,4 @@
 require 'spec_helper'
-require_relative 'import_spec_helper'
 
 describe Import::GitoriousController do
   include ImportSpecHelper
diff --git a/spec/controllers/import/google_code_controller_spec.rb b/spec/controllers/import/google_code_controller_spec.rb
index 66088139a69bb06778d8d2cbbe96b1634e55aa79..4241db6e771a83e9ff0322e57871e7fe3161de28 100644
--- a/spec/controllers/import/google_code_controller_spec.rb
+++ b/spec/controllers/import/google_code_controller_spec.rb
@@ -1,5 +1,4 @@
 require 'spec_helper'
-require_relative 'import_spec_helper'
 
 describe Import::GoogleCodeController do
   include ImportSpecHelper
diff --git a/spec/controllers/oauth/applications_controller_spec.rb b/spec/controllers/oauth/applications_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..af3783048931c255bf070582412e8bf22df7b427
--- /dev/null
+++ b/spec/controllers/oauth/applications_controller_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+
+describe Oauth::ApplicationsController do
+  let(:user) { create(:user) }
+
+  context 'project members' do
+    before do
+      sign_in(user)
+    end
+
+    describe 'GET #index' do
+      it 'shows list of applications' do
+        get :index
+
+        expect(response.status).to eq(200)
+      end
+
+      it 'redirects back to profile page if OAuth applications are disabled' do
+        settings = double(user_oauth_applications?: false)
+        allow_any_instance_of(Gitlab::CurrentSettings).to receive(:current_application_settings).and_return(settings)
+
+        get :index
+
+        expect(response.status).to eq(302)
+        expect(response).to redirect_to(profile_path)
+      end
+    end
+  end
+end
diff --git a/spec/controllers/profiles/two_factor_auths_controller_spec.rb b/spec/controllers/profiles/two_factor_auths_controller_spec.rb
index 4fb1473c2d264ffc651487a2a8217c9d2879c65b..d08d0018b359a40bcf192afa6f490e2b0c84b9f0 100644
--- a/spec/controllers/profiles/two_factor_auths_controller_spec.rb
+++ b/spec/controllers/profiles/two_factor_auths_controller_spec.rb
@@ -8,21 +8,21 @@ describe Profiles::TwoFactorAuthsController do
     allow(subject).to receive(:current_user).and_return(user)
   end
 
-  describe 'GET new' do
+  describe 'GET show' do
     let(:user) { create(:user) }
 
     it 'generates otp_secret for user' do
       expect(User).to receive(:generate_otp_secret).with(32).and_return('secret').once
 
-      get :new
-      get :new # Second hit shouldn't re-generate it
+      get :show
+      get :show # Second hit shouldn't re-generate it
     end
 
     it 'assigns qr_code' do
       code = double('qr code')
       expect(subject).to receive(:build_qr_code).and_return(code)
 
-      get :new
+      get :show
       expect(assigns[:qr_code]).to eq code
     end
   end
@@ -40,7 +40,7 @@ describe Profiles::TwoFactorAuthsController do
         expect(user).to receive(:validate_and_consume_otp!).with(pin).and_return(true)
       end
 
-      it 'sets two_factor_enabled' do
+      it 'enables 2fa for the user' do
         go
 
         user.reload
@@ -79,9 +79,9 @@ describe Profiles::TwoFactorAuthsController do
         expect(assigns[:qr_code]).to eq code
       end
 
-      it 'renders new' do
+      it 'renders show' do
         go
-        expect(response).to render_template(:new)
+        expect(response).to render_template(:show)
       end
     end
   end
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index 8ad734721170d18c40bed4987f85acb074dbf312..c4b4a888b4ed4d010fe89e911aebc3d782963af0 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -122,27 +122,23 @@ describe Projects::BranchesController do
       let(:branch) { "feature" }
 
       it { expect(response.status).to eq(200) }
-      it { expect(subject).to render_template('destroy') }
     end
 
     context "valid branch name with unencoded slashes" do
       let(:branch) { "improve/awesome" }
 
       it { expect(response.status).to eq(200) }
-      it { expect(subject).to render_template('destroy') }
     end
 
     context "valid branch name with encoded slashes" do
       let(:branch) { "improve%2Fawesome" }
 
       it { expect(response.status).to eq(200) }
-      it { expect(subject).to render_template('destroy') }
     end
     context "invalid branch name, valid ref" do
       let(:branch) { "no-branch" }
 
       it { expect(response.status).to eq(404) }
-      it { expect(subject).to render_template('destroy') }
     end
   end
 end
diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb
index 438e776ec4b9b9808e8ca0b8ed663db6644ee959..6e3db10e451af4fd066fc977c40af2eaac9a703e 100644
--- a/spec/controllers/projects/commit_controller_spec.rb
+++ b/spec/controllers/projects/commit_controller_spec.rb
@@ -2,6 +2,8 @@ require 'rails_helper'
 
 describe Projects::CommitController do
   describe 'GET show' do
+    render_views
+
     let(:project) { create(:project) }
 
     before do
@@ -27,6 +29,16 @@ describe Projects::CommitController do
       end
     end
 
+    it 'handles binary files' do
+      get(:show,
+          namespace_id: project.namespace.to_param,
+          project_id: project.to_param,
+          id: TestEnv::BRANCH_SHA['binary-encoding'],
+          format: "html")
+
+      expect(response).to be_success
+    end
+
     def go(id:)
       get :show,
         namespace_id: project.namespace.to_param,
diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb
index 788a609ee40230f53ced6c455fc136a761f54ec9..4018dac95a2e93b5f01d3c78ae58d1d038c99f5c 100644
--- a/spec/controllers/projects/compare_controller_spec.rb
+++ b/spec/controllers/projects/compare_controller_spec.rb
@@ -19,7 +19,7 @@ describe Projects::CompareController do
         to: ref_to)
 
     expect(response).to be_success
-    expect(assigns(:diffs).first).to_not be_nil
+    expect(assigns(:diffs).first).not_to be_nil
     expect(assigns(:commits).length).to be >= 1
   end
 
@@ -32,7 +32,7 @@ describe Projects::CompareController do
         w: 1)
 
     expect(response).to be_success
-    expect(assigns(:diffs).first).to_not be_nil
+    expect(assigns(:diffs).first).not_to be_nil
     expect(assigns(:commits).length).to be >= 1
     # without whitespace option, there are more than 2 diff_splits
     diff_splits = assigns(:diffs).first.diff.split("\n")
diff --git a/spec/controllers/projects/group_links_controller_spec.rb b/spec/controllers/projects/group_links_controller_spec.rb
index 40bd83af8613f06ba6a006c5d49bd81be3e20944..fbe8758dda7333839c1c863c596cb91341faa3a1 100644
--- a/spec/controllers/projects/group_links_controller_spec.rb
+++ b/spec/controllers/projects/group_links_controller_spec.rb
@@ -28,7 +28,7 @@ describe Projects::GroupLinksController do
         expect(group.shared_projects).to include project
       end
 
-      it 'redirects to project group links page'do
+      it 'redirects to project group links page' do
         expect(response).to redirect_to(
           namespace_project_group_links_path(project.namespace, project)
         )
@@ -43,7 +43,7 @@ describe Projects::GroupLinksController do
       end
 
       it 'does not share project with that group' do
-        expect(group.shared_projects).to_not include project
+        expect(group.shared_projects).not_to include project
       end
     end
   end
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 2b2ad3b9412ab3a78b2fc3fc03b0e9c1c98bb59c..cbaa3e0b7b2a537b405b201083cfc037576d0855 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -56,7 +56,7 @@ describe Projects::IssuesController do
           move_issue
 
           expect(response).to have_http_status :found
-          expect(another_project.issues).to_not be_empty
+          expect(another_project.issues).not_to be_empty
         end
       end
 
@@ -105,6 +105,15 @@ describe Projects::IssuesController do
         expect(assigns(:issues)).to eq [issue]
       end
 
+      it 'should not list confidential issues for project members with guest role' do
+        sign_in(member)
+        project.team << [member, :guest]
+
+        get_issues
+
+        expect(assigns(:issues)).to eq [issue]
+      end
+
       it 'should list confidential issues for author' do
         sign_in(author)
         get_issues
@@ -148,7 +157,7 @@ describe Projects::IssuesController do
 
     shared_examples_for 'restricted action' do |http_status|
       it 'returns 404 for guests' do
-        sign_out :user
+        sign_out(:user)
         go(id: unescaped_parameter_value.to_param)
 
         expect(response).to have_http_status :not_found
@@ -161,6 +170,14 @@ describe Projects::IssuesController do
         expect(response).to have_http_status :not_found
       end
 
+      it 'returns 404 for project members with guest role' do
+        sign_in(member)
+        project.team << [member, :guest]
+        go(id: unescaped_parameter_value.to_param)
+
+        expect(response).to have_http_status :not_found
+      end
+
       it "returns #{http_status[:success]} for author" do
         sign_in(author)
         go(id: unescaped_parameter_value.to_param)
@@ -250,4 +267,20 @@ describe Projects::IssuesController do
       end
     end
   end
+
+  describe 'POST #toggle_award_emoji' do
+    before do
+      sign_in(user)
+      project.team << [user, :developer]
+    end
+
+    it "toggles the award emoji" do
+      expect do
+        post(:toggle_award_emoji, namespace_id: project.namespace.path,
+                                  project_id: project.path, id: issue.iid, name: "thumbsup")
+      end.to change { issue.award_emoji.count }.by(1)
+
+      expect(response.status).to eq(200)
+    end
+  end
 end
diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ab1dd34ed575b74496832482bd1534ff8abc4377
--- /dev/null
+++ b/spec/controllers/projects/labels_controller_spec.rb
@@ -0,0 +1,53 @@
+require 'spec_helper'
+
+describe Projects::LabelsController do
+  let(:project) { create(:project) }
+  let(:user)    { create(:user) }
+
+  before do
+    project.team << [user, :master]
+    sign_in(user)
+  end
+
+  describe 'GET #index' do
+    def create_label(attributes)
+      create(:label, attributes.merge(project: project))
+    end
+
+    before do
+      15.times { |i| create_label(priority: (i % 3) + 1, title: "label #{15 - i}") }
+      5.times { |i| create_label(title: "label #{100 - i}") }
+
+
+      get :index, namespace_id: project.namespace.to_param, project_id: project.to_param
+    end
+
+    context '@prioritized_labels' do
+      let(:prioritized_labels) { assigns(:prioritized_labels) }
+
+      it 'contains only prioritized labels' do
+        expect(prioritized_labels).to all(have_attributes(priority: a_value > 0))
+      end
+
+      it 'is sorted by priority, then label title' do
+        priorities_and_titles = prioritized_labels.pluck(:priority, :title)
+
+        expect(priorities_and_titles.sort).to eq(priorities_and_titles)
+      end
+    end
+
+    context '@labels' do
+      let(:labels) { assigns(:labels) }
+
+      it 'contains only unprioritized labels' do
+        expect(labels).to all(have_attributes(priority: nil))
+      end
+
+      it 'is sorted by label title' do
+        titles = labels.pluck(:title)
+
+        expect(titles.sort).to eq(titles)
+      end
+    end
+  end
+end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index c0a1f45195f8a9eb2ed7e3a38406c6e9d8d5ed23..3fba6b2d4bf52ea9bf23e0148e20808216897c30 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -63,7 +63,7 @@ describe Projects::MergeRequestsController do
             id: merge_request.iid,
             format: format)
 
-        expect(response.body).to eq((merge_request.send(:"to_#{format}")).to_s)
+        expect(response.body).to eq(merge_request.send(:"to_#{format}").to_s)
       end
 
       it "should not escape Html" do
@@ -84,17 +84,14 @@ describe Projects::MergeRequestsController do
     end
 
     describe "as diff" do
-      include_examples "export merge as", :diff
-      let(:format) { :diff }
-
-      it "should really only be a git diff" do
+      it "triggers workhorse to serve the request" do
         get(:show,
             namespace_id: project.namespace.to_param,
             project_id: project.to_param,
             id: merge_request.iid,
-            format: format)
+            format: :diff)
 
-        expect(response.body).to start_with("diff --git")
+        expect(response.headers[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-diff:")
       end
     end
 
@@ -185,6 +182,135 @@ describe Projects::MergeRequestsController do
     end
   end
 
+  describe 'POST #merge' do
+    let(:base_params) do
+      {
+        namespace_id: project.namespace.path,
+        project_id: project.path,
+        id: merge_request.iid,
+        format: 'raw'
+      }
+    end
+
+    context 'when the user does not have access' do
+      before do
+        project.team.truncate
+        project.team << [user, :reporter]
+        post :merge, base_params
+      end
+
+      it 'returns not found' do
+        expect(response).to be_not_found
+      end
+    end
+
+    context 'when the merge request is not mergeable' do
+      before do
+        merge_request.update_attributes(title: "WIP: #{merge_request.title}")
+
+        post :merge, base_params
+      end
+
+      it 'returns :failed' do
+        expect(assigns(:status)).to eq(:failed)
+      end
+    end
+
+    context 'when the sha parameter does not match the source SHA' do
+      before { post :merge, base_params.merge(sha: 'foo') }
+
+      it 'returns :sha_mismatch' do
+        expect(assigns(:status)).to eq(:sha_mismatch)
+      end
+    end
+
+    context 'when the sha parameter matches the source SHA' do
+      def merge_with_sha
+        post :merge, base_params.merge(sha: merge_request.source_sha)
+      end
+
+      it 'returns :success' do
+        merge_with_sha
+
+        expect(assigns(:status)).to eq(:success)
+      end
+
+      it 'starts the merge immediately' do
+        expect(MergeWorker).to receive(:perform_async).with(merge_request.id, anything, anything)
+
+        merge_with_sha
+      end
+
+      context 'when merge_when_build_succeeds is passed' do
+        def merge_when_build_succeeds
+          post :merge, base_params.merge(sha: merge_request.source_sha, merge_when_build_succeeds: '1')
+        end
+
+        before do
+          create(:ci_empty_pipeline, project: project, sha: merge_request.source_sha, ref: merge_request.source_branch)
+        end
+
+        it 'returns :merge_when_build_succeeds' do
+          merge_when_build_succeeds
+
+          expect(assigns(:status)).to eq(:merge_when_build_succeeds)
+        end
+
+        it 'sets the MR to merge when the build succeeds' do
+          service = double(:merge_when_build_succeeds_service)
+
+          expect(MergeRequests::MergeWhenBuildSucceedsService).to receive(:new).with(project, anything, anything).and_return(service)
+          expect(service).to receive(:execute).with(merge_request)
+
+          merge_when_build_succeeds
+        end
+      end
+    end
+  end
+
+  describe 'POST #approve' do
+    def approve(user)
+      post :approve, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid, user: user
+    end
+
+    context 'when the user is the author of the MR' do
+      before { merge_request.approvers.create(user: merge_request.author) }
+
+      it "returns a 404" do
+        approve(merge_request.author)
+
+        expect(response).to have_http_status(404)
+      end
+    end
+
+    context 'when the user is not allowed to approve the MR' do
+      it "returns a 404" do
+        approve(user)
+
+        expect(response).to have_http_status(404)
+      end
+    end
+
+    context 'when the user is allowed to approve the MR' do
+      before { merge_request.approvers.create(user: user) }
+
+      it 'creates an approval' do
+        service = double(:approval_service)
+
+        expect(MergeRequests::ApprovalService).to receive(:new).with(project, anything).and_return(service)
+        expect(service).to receive(:execute).with(merge_request)
+
+        approve(user)
+      end
+
+      it 'redirects to the MR' do
+        approve(user)
+
+        expect(response).to redirect_to(namespace_project_merge_request_path)
+      end
+    end
+  end
+
   describe "DELETE #destroy" do
     it "denies access to users unless they're admin or project owner" do
       delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid
diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..00bc38b60714e5a33da21d61c532551d311a746b
--- /dev/null
+++ b/spec/controllers/projects/notes_controller_spec.rb
@@ -0,0 +1,36 @@
+require('spec_helper')
+
+describe Projects::NotesController do
+  let(:user)    { create(:user) }
+  let(:project) { create(:project) }
+  let(:issue)   { create(:issue, project: project) }
+  let(:note)    { create(:note, noteable: issue, project: project) }
+
+  describe 'POST #toggle_award_emoji' do
+    before do
+      sign_in(user)
+      project.team << [user, :developer]
+    end
+
+    it "toggles the award emoji" do
+      expect do
+        post(:toggle_award_emoji, namespace_id: project.namespace.path,
+                                  project_id: project.path, id: note.id, name: "thumbsup")
+      end.to change { note.award_emoji.count }.by(1)
+
+      expect(response.status).to eq(200)
+    end
+
+    it "removes the already awarded emoji" do
+      post(:toggle_award_emoji, namespace_id: project.namespace.path,
+                                project_id: project.path, id: note.id, name: "thumbsup")
+
+      expect do
+        post(:toggle_award_emoji, namespace_id: project.namespace.path,
+                                  project_id: project.path, id: note.id, name: "thumbsup")
+      end.to change { AwardEmoji.count }.by(-1)
+
+      expect(response.status).to eq(200)
+    end
+  end
+end
diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb
index ed64e7cf9af67b8c6388907b529e6ae38103a4a9..fc5f458e79543b3496af06c64b6e6fe380716492 100644
--- a/spec/controllers/projects/project_members_controller_spec.rb
+++ b/spec/controllers/projects/project_members_controller_spec.rb
@@ -1,22 +1,22 @@
 require('spec_helper')
 
 describe Projects::ProjectMembersController do
-  let(:project) { create(:project) }
-  let(:another_project) { create(:project, :private) }
-  let(:user) { create(:user) }
-  let(:member) { create(:user) }
-
-  before do
-    project.team << [user, :master]
-    another_project.team << [member, :guest]
-    sign_in(user)
-  end
-
   describe '#apply_import' do
+    let(:project) { create(:project) }
+    let(:another_project) { create(:project, :private) }
+    let(:user) { create(:user) }
+    let(:member) { create(:user) }
+
+    before do
+      project.team << [user, :master]
+      another_project.team << [member, :guest]
+      sign_in(user)
+    end
+
     shared_context 'import applied' do
       before do
-        post(:apply_import, namespace_id: project.namespace.to_param,
-                            project_id: project.to_param,
+        post(:apply_import, namespace_id: project.namespace,
+                            project_id: project,
                             source_project_id: another_project.id)
       end
     end
@@ -38,7 +38,7 @@ describe Projects::ProjectMembersController do
       include_context 'import applied'
 
       it 'does not import team members' do
-        expect(project.team_members).to_not include member
+        expect(project.team_members).not_to include member
       end
 
       it 'responds with not found' do
@@ -48,18 +48,231 @@ describe Projects::ProjectMembersController do
   end
 
   describe '#index' do
-    let(:project) { create(:project, :private) }
-
     context 'when user is member' do
-      let(:member) { create(:user) }
-
       before do
+        project = create(:project, :private)
+        member = create(:user)
         project.team << [member, :guest]
         sign_in(member)
-        get :index, namespace_id: project.namespace.to_param, project_id: project.to_param
+
+        get :index, namespace_id: project.namespace, project_id: project
       end
 
       it { expect(response.status).to eq(200) }
     end
   end
+
+  describe '#destroy' do
+    let(:project) { create(:project, :public) }
+
+    context 'when member is not found' do
+      it 'returns 404' do
+        delete :destroy, namespace_id: project.namespace,
+                         project_id: project,
+                         id: 42
+
+        expect(response.status).to eq(404)
+      end
+    end
+
+    context 'when member is found' do
+      let(:user) { create(:user) }
+      let(:team_user) { create(:user) }
+      let(:member) do
+        project.team << [team_user, :developer]
+        project.members.find_by(user_id: team_user.id)
+      end
+
+      context 'when user does not have enough rights' do
+        before do
+          project.team << [user, :developer]
+          sign_in(user)
+        end
+
+        it 'returns 404' do
+          delete :destroy, namespace_id: project.namespace,
+                           project_id: project,
+                           id: member
+
+          expect(response.status).to eq(404)
+          expect(project.users).to include team_user
+        end
+      end
+
+      context 'when user has enough rights' do
+        before do
+          project.team << [user, :master]
+          sign_in(user)
+        end
+
+        it '[HTML] removes user from members' do
+          delete :destroy, namespace_id: project.namespace,
+                           project_id: project,
+                           id: member
+
+          expect(response).to redirect_to(
+            namespace_project_project_members_path(project.namespace, project)
+          )
+          expect(project.users).not_to include team_user
+        end
+
+        it '[JS] removes user from members' do
+          xhr :delete, :destroy, namespace_id: project.namespace,
+                                 project_id: project,
+                                 id: member
+
+          expect(response).to be_success
+          expect(project.users).not_to include team_user
+        end
+      end
+    end
+  end
+
+  describe '#leave' do
+    let(:project) { create(:project, :public) }
+    let(:user) { create(:user) }
+
+    context 'when member is not found' do
+      before { sign_in(user) }
+
+      it 'returns 403' do
+        delete :leave, namespace_id: project.namespace,
+                       project_id: project
+
+        expect(response.status).to eq(403)
+      end
+    end
+
+    context 'when member is found' do
+      context 'and is not an owner' do
+        before do
+          project.team << [user, :developer]
+          sign_in(user)
+        end
+
+        it 'removes user from members' do
+          delete :leave, namespace_id: project.namespace,
+                         project_id: project
+
+          expect(response).to set_flash.to "You left the \"#{project.human_name}\" project."
+          expect(response).to redirect_to(dashboard_projects_path)
+          expect(project.users).not_to include user
+        end
+      end
+
+      context 'and is an owner' do
+        before do
+          project.update(namespace_id: user.namespace_id)
+          project.team << [user, :master, user]
+          sign_in(user)
+        end
+
+        it 'cannot remove himself from the project' do
+          delete :leave, namespace_id: project.namespace,
+                         project_id: project
+
+          expect(response).to redirect_to(
+            namespace_project_path(project.namespace, project)
+          )
+          expect(response).to set_flash[:alert].to "You can not leave the \"#{project.human_name}\" project. Transfer or delete the project."
+          expect(project.users).to include user
+        end
+      end
+
+      context 'and is a requester' do
+        before do
+          project.request_access(user)
+          sign_in(user)
+        end
+
+        it 'removes user from members' do
+          delete :leave, namespace_id: project.namespace,
+                         project_id: project
+
+          expect(response).to set_flash.to 'Your access request to the project has been withdrawn.'
+          expect(response).to redirect_to(dashboard_projects_path)
+          expect(project.members.request).to be_empty
+          expect(project.users).not_to include user
+        end
+      end
+    end
+  end
+
+  describe '#request_access' do
+    let(:project) { create(:project, :public) }
+    let(:user) { create(:user) }
+
+    before do
+      sign_in(user)
+    end
+
+    it 'creates a new ProjectMember that is not a team member' do
+      post :request_access, namespace_id: project.namespace,
+                            project_id: project
+
+      expect(response).to set_flash.to 'Your request for access has been queued for review.'
+      expect(response).to redirect_to(
+        namespace_project_path(project.namespace, project)
+      )
+      expect(project.members.request.exists?(user_id: user)).to be_truthy
+      expect(project.users).not_to include user
+    end
+  end
+
+  describe '#approve' do
+    let(:project) { create(:project, :public) }
+
+    context 'when member is not found' do
+      it 'returns 404' do
+        post :approve_access_request, namespace_id: project.namespace,
+                                      project_id: project,
+                                      id: 42
+
+        expect(response.status).to eq(404)
+      end
+    end
+
+    context 'when member is found' do
+      let(:user) { create(:user) }
+      let(:team_requester) { create(:user) }
+      let(:member) do
+        project.request_access(team_requester)
+        project.members.request.find_by(user_id: team_requester.id)
+      end
+
+      context 'when user does not have enough rights' do
+        before do
+          project.team << [user, :developer]
+          sign_in(user)
+        end
+
+        it 'returns 404' do
+          post :approve_access_request, namespace_id: project.namespace,
+                                        project_id: project,
+                                        id: member
+
+          expect(response.status).to eq(404)
+          expect(project.users).not_to include team_requester
+        end
+      end
+
+      context 'when user has enough rights' do
+        before do
+          project.team << [user, :master]
+          sign_in(user)
+        end
+
+        it 'adds user to members' do
+          post :approve_access_request, namespace_id: project.namespace,
+                                        project_id: project,
+                                        id: member
+
+          expect(response).to redirect_to(
+            namespace_project_project_members_path(project.namespace, project)
+          )
+          expect(project.users).to include team_requester
+        end
+      end
+    end
+  end
 end
diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb
index fb29274c687676bf92c5df5071624661a458610e..33c35161da3fbf3a8468af40291c77169ad4a033 100644
--- a/spec/controllers/projects/raw_controller_spec.rb
+++ b/spec/controllers/projects/raw_controller_spec.rb
@@ -17,6 +17,7 @@ describe Projects::RawController do
         expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
         expect(response.header['Content-Disposition']).
             to eq("inline")
+        expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-blob:")
       end
     end
 
@@ -31,6 +32,7 @@ describe Projects::RawController do
 
         expect(response.status).to eq(200)
         expect(response.header['Content-Type']).to eq('image/jpeg')
+        expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-blob:")
       end
     end
 
diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb
index 0ddbec9eac21cee9bba8ff16f8e90cc310a04f2e..aad62cf20e3e187e2c0a8656ff0edc1987c7b7b2 100644
--- a/spec/controllers/projects/repositories_controller_spec.rb
+++ b/spec/controllers/projects/repositories_controller_spec.rb
@@ -20,10 +20,11 @@ describe Projects::RepositoriesController do
         project.team << [user, :developer]
         sign_in(user)
       end
-      it "uses Gitlab::Workhorse" do
-        expect(Gitlab::Workhorse).to receive(:send_git_archive).with(project, "master", "zip")
 
+      it "uses Gitlab::Workhorse" do
         get :archive, namespace_id: project.namespace.path, project_id: project.path, ref: "master", format: "zip"
+
+        expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-archive:")
       end
 
       context "when the service raises an error" do
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 91b46c4d65c452d8838a27b8be53b79aff00263b..fba545560c7a53f9b3b7eb037ce5d3b1121b8d9a 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -115,6 +115,17 @@ describe ProjectsController do
         expect(public_project_with_dot_atom).not_to be_valid
       end
     end
+
+    context 'when the project is pending deletions' do
+      it 'renders a 404 error' do
+        project = create(:project, pending_delete: true)
+        sign_in(user)
+
+        get :show, namespace_id: project.namespace.path, id: project.path
+
+        expect(response.status).to eq 404
+      end
+    end
   end
 
   describe "#update" do
diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb
index df70a589a89cb7a7b620652d3720a0252b38410d..209fa37d97d9209ebd70766a268584b1ad5ccec8 100644
--- a/spec/controllers/registrations_controller_spec.rb
+++ b/spec/controllers/registrations_controller_spec.rb
@@ -11,17 +11,17 @@ describe RegistrationsController do
     let(:user_params) { { user: { name: "new_user", username: "new_username", email: "new@user.com", password: "Any_password" } } }
 
     context 'when sending email confirmation' do
-      before { allow(current_application_settings).to receive(:send_user_confirmation_email).and_return(false) }
+      before { allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(false) }
 
       it 'logs user in directly' do
         post(:create, user_params)
         expect(ActionMailer::Base.deliveries.last).to be_nil
-        expect(subject.current_user).to_not be_nil
+        expect(subject.current_user).not_to be_nil
       end
     end
 
     context 'when not sending email confirmation' do
-      before { allow(current_application_settings).to receive(:send_user_confirmation_email).and_return(true) }
+      before { allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(true) }
 
       it 'does not authenticate user and sends confirmation email' do
         post(:create, user_params)
diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb
index 83cc8ec6d26dc0fa00a8c177f261dd5e4cfd7c91..4e9bfb0c69b747d717b458fd8e5a1042b625887a 100644
--- a/spec/controllers/sessions_controller_spec.rb
+++ b/spec/controllers/sessions_controller_spec.rb
@@ -12,7 +12,7 @@ describe SessionsController do
           post(:create, user: { login: 'invalid', password: 'invalid' })
 
           expect(response)
-            .to set_flash.now[:alert].to /Invalid login or password/
+            .to set_flash.now[:alert].to /Invalid Login or password/
         end
       end
 
@@ -25,16 +25,42 @@ describe SessionsController do
           expect(response).to set_flash.to /Signed in successfully/
           expect(subject.current_user). to eq user
         end
+
+        it "creates an audit log record" do
+          expect { post(:create, user: { login: user.username, password: user.password }) }.to change { SecurityEvent.count }.by(1)
+          expect(SecurityEvent.last.details[:with]).to eq("standard")
+        end
       end
     end
 
-    context 'when using two-factor authentication' do
+    context 'when using two-factor authentication via OTP' do
       let(:user) { create(:user, :two_factor) }
 
       def authenticate_2fa(user_params)
         post(:create, { user: user_params }, { otp_user_id: user.id })
       end
 
+      context 'remember_me field' do
+        it 'sets a remember_user_token cookie when enabled' do
+          allow(controller).to receive(:find_user).and_return(user)
+          expect(controller).
+            to receive(:remember_me).with(user).and_call_original
+
+          authenticate_2fa(remember_me: '1', otp_attempt: user.current_otp)
+
+          expect(response.cookies['remember_user_token']).to be_present
+        end
+
+        it 'does nothing when disabled' do
+          allow(controller).to receive(:find_user).and_return(user)
+          expect(controller).not_to receive(:remember_me)
+
+          authenticate_2fa(remember_me: '0', otp_attempt: user.current_otp)
+
+          expect(response.cookies['remember_user_token']).to be_nil
+        end
+      end
+
       ##
       # See #14900 issue
       #
@@ -47,7 +73,7 @@ describe SessionsController do
               authenticate_2fa(login: another_user.username,
                                otp_attempt: another_user.current_otp)
 
-              expect(subject.current_user).to_not eq another_user
+              expect(subject.current_user).not_to eq another_user
             end
           end
 
@@ -56,7 +82,7 @@ describe SessionsController do
               authenticate_2fa(login: another_user.username,
                                otp_attempt: 'invalid')
 
-              expect(subject.current_user).to_not eq another_user
+              expect(subject.current_user).not_to eq another_user
             end
           end
 
@@ -73,7 +99,7 @@ describe SessionsController do
               before { authenticate_2fa(otp_attempt: 'invalid') }
 
               it 'does not authenticate' do
-                expect(subject.current_user).to_not eq user
+                expect(subject.current_user).not_to eq user
               end
 
               it 'warns about invalid OTP code' do
@@ -96,6 +122,25 @@ describe SessionsController do
           end
         end
       end
+
+      it "creates an audit log record" do
+        expect { authenticate_2fa(login: user.username, otp_attempt: user.current_otp) }.to change { SecurityEvent.count }.by(1)
+        expect(SecurityEvent.last.details[:with]).to eq("two-factor")
+      end
+    end
+
+    context 'when using two-factor authentication via U2F device' do
+      let(:user) { create(:user, :two_factor) }
+
+      def authenticate_2fa_u2f(user_params)
+        post(:create, { user: user_params }, { otp_user_id: user.id })
+      end
+
+      it "creates an audit log record" do
+        allow(U2fRegistration).to receive(:authenticate).and_return(true)
+        expect { authenticate_2fa_u2f(login: user.username, device_response: "{}") }.to change { SecurityEvent.count }.by(1)
+        expect(SecurityEvent.last.details[:with]).to eq("two-factor-via-u2f-device")
+      end
     end
   end
 end
diff --git a/spec/factories/award_emoji.rb b/spec/factories/award_emoji.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4b858df52c9bb9b82832b50b25984672022f0494
--- /dev/null
+++ b/spec/factories/award_emoji.rb
@@ -0,0 +1,12 @@
+FactoryGirl.define do
+  factory :award_emoji do
+    name "thumbsup"
+    user
+    awardable factory: :issue
+
+    trait :upvote
+    trait :downvote do
+      name "thumbsdown"
+    end
+  end
+end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index cd49e559b7dbc0cc18f08560162b78b6138d5d9a..fe05a0cfc001977e51100f58a15f6c238dd462cc 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -16,7 +16,7 @@ FactoryGirl.define do
       }
     end
 
-    commit factory: :ci_commit
+    pipeline factory: :ci_pipeline
 
     trait :success do
       status 'success'
@@ -43,7 +43,7 @@ FactoryGirl.define do
     end
 
     after(:build) do |build, evaluator|
-      build.project = build.commit.project
+      build.project = build.pipeline.project
     end
 
     factory :ci_not_started_build do
diff --git a/spec/factories/ci/commits.rb b/spec/factories/ci/commits.rb
index 645cd7ae766d9bd03f313917ff1e1723a6dd857a..a039bef6f3c44ec1751c8566713b0d76f5d63e12 100644
--- a/spec/factories/ci/commits.rb
+++ b/spec/factories/ci/commits.rb
@@ -17,30 +17,30 @@
 #
 
 FactoryGirl.define do
-  factory :ci_empty_commit, class: Ci::Commit do
+  factory :ci_empty_pipeline, class: Ci::Pipeline do
     sha '97de212e80737a608d939f648d959671fb0a0142'
 
     project factory: :empty_project
 
-    factory :ci_commit_without_jobs do
+    factory :ci_pipeline_without_jobs do
       after(:build) do |commit|
         allow(commit).to receive(:ci_yaml_file) { YAML.dump({}) }
       end
     end
 
-    factory :ci_commit_with_one_job do
+    factory :ci_pipeline_with_one_job do
       after(:build) do |commit|
         allow(commit).to receive(:ci_yaml_file) { YAML.dump({ rspec: { script: "ls" } }) }
       end
     end
 
-    factory :ci_commit_with_two_jobs do
+    factory :ci_pipeline_with_two_job do
       after(:build) do |commit|
         allow(commit).to receive(:ci_yaml_file) { YAML.dump({ rspec: { script: "ls" }, spinach: { script: "ls" } }) }
       end
     end
 
-    factory :ci_commit do
+    factory :ci_pipeline do
       after(:build) do |commit|
         allow(commit).to receive(:ci_yaml_file) { File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) }
       end
diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb
index b7c2b32cb13d07a7f6e0c946f7255816d76a9de9..1e5c479616c50b0531cee190035f85d8bbd51757 100644
--- a/spec/factories/commit_statuses.rb
+++ b/spec/factories/commit_statuses.rb
@@ -3,12 +3,12 @@ FactoryGirl.define do
     name 'default'
     status 'success'
     description 'commit status'
-    commit factory: :ci_commit_with_one_job
+    pipeline factory: :ci_pipeline_with_one_job
     started_at 'Tue, 26 Jan 2016 08:21:42 +0100'
     finished_at 'Tue, 26 Jan 2016 08:23:42 +0100'
 
     after(:build) do |build, evaluator|
-      build.project = build.commit.project
+      build.project = build.pipeline.project
     end
 
     factory :generic_commit_status, class: GenericCommitStatus do
diff --git a/spec/factories/deployments.rb b/spec/factories/deployments.rb
new file mode 100644
index 0000000000000000000000000000000000000000..82591604fcb313c77789f7d0117c2f98a1df56c8
--- /dev/null
+++ b/spec/factories/deployments.rb
@@ -0,0 +1,13 @@
+FactoryGirl.define do
+  factory :deployment, class: Deployment do
+    sha '97de212e80737a608d939f648d959671fb0a0142'
+    ref 'master'
+    tag false
+
+    environment factory: :environment
+
+    after(:build) do |deployment, evaluator|
+      deployment.project = deployment.environment.project
+    end
+  end
+end
diff --git a/spec/factories/environments.rb b/spec/factories/environments.rb
new file mode 100644
index 0000000000000000000000000000000000000000..07265c26ca3dcdcbd540383e06cfb5118d036700
--- /dev/null
+++ b/spec/factories/environments.rb
@@ -0,0 +1,7 @@
+FactoryGirl.define do
+  factory :environment, class: Environment do
+    sequence(:name) { |n| "environment#{n}" }
+
+    project factory: :empty_project
+  end
+end
diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb
index 03dc723f4ac8380d780cbcc57ac30fb2096c0dc5..696cf276e57032d272af6345d937825396eafb76 100644
--- a/spec/factories/notes.rb
+++ b/spec/factories/notes.rb
@@ -7,6 +7,7 @@ FactoryGirl.define do
     project
     note "Note"
     author
+    on_issue
 
     factory :note_on_commit,             traits: [:on_commit]
     factory :note_on_commit_diff,        traits: [:on_commit, :on_diff], class: LegacyDiffNote
@@ -15,44 +16,34 @@ FactoryGirl.define do
     factory :note_on_merge_request_diff, traits: [:on_merge_request, :on_diff], class: LegacyDiffNote
     factory :note_on_project_snippet,    traits: [:on_project_snippet]
     factory :system_note,                traits: [:system]
-    factory :downvote_note,              traits: [:award, :downvote]
-    factory :upvote_note,                traits: [:award, :upvote]
 
     trait :on_commit do
-      project
+      noteable nil
+      noteable_id nil
+      noteable_type 'Commit'
       commit_id RepoHelpers.sample_commit.id
-      noteable_type "Commit"
     end
 
     trait :on_diff do
       line_code "0_184_184"
     end
 
-    trait :on_merge_request do
-      project
-      noteable_id 1
-      noteable_type "MergeRequest"
+    trait :on_issue do
+      noteable { create(:issue, project: project) }
     end
 
-    trait :on_issue do
-      noteable_id 1
-      noteable_type "Issue"
+    trait :on_merge_request do
+      noteable { create(:merge_request, source_project: project) }
     end
 
     trait :on_project_snippet do
-      noteable_id 1
-      noteable_type "Snippet"
+      noteable { create(:snippet, project: project) }
     end
 
     trait :system do
       system true
     end
 
-    trait :award do
-      is_award true
-      note Emoji.emojis_names.sample
-    end
-
     trait :downvote do
       note "thumbsdown"
     end
diff --git a/spec/factories/path_locks.rb b/spec/factories/path_locks.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7f844ff8e0290edae1825ba0bb07befcff571516
--- /dev/null
+++ b/spec/factories/path_locks.rb
@@ -0,0 +1,7 @@
+FactoryGirl.define do
+  factory :path_lock do
+    project
+    user { build :user }
+    sequence(:path) { |n| "app/model#{n}" }
+  end
+end
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 94cb8a401c140922973b38a53afbdbf8cde36fe2..6e547297df73e09d684afa8e659a35a0b59afdf6 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -84,9 +84,6 @@ FactoryGirl.define do
           'new_issue_url' => 'http://redmine/projects/project_name_in_redmine/issues/new'
         }
       )
-
-      project.issues_tracker = 'redmine'
-      project.issues_tracker_id = 'project_name_in_redmine'
     end
   end
 
@@ -101,9 +98,6 @@ FactoryGirl.define do
           'new_issue_url' => 'http://jira.example/secure/CreateIssue.jspa'
         }
       )
-
-      project.issues_tracker = 'jira'
-      project.issues_tracker_id = 'project_name_in_jira'
     end
   end
 end
diff --git a/spec/factories/u2f_registrations.rb b/spec/factories/u2f_registrations.rb
new file mode 100644
index 0000000000000000000000000000000000000000..df92b0795814e9a8e58c82353a2b66a29974a623
--- /dev/null
+++ b/spec/factories/u2f_registrations.rb
@@ -0,0 +1,8 @@
+FactoryGirl.define do
+  factory :u2f_registration do
+    certificate { FFaker::BaconIpsum.characters(728) }
+    key_handle { FFaker::BaconIpsum.characters(86) }
+    public_key { FFaker::BaconIpsum.characters(88) }
+    counter 0
+  end
+end
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index a9b2148bd2ad477aa7dece6e41ca86ba417545fe..c6f7869516e207f03a22a094660e3894eacbe45e 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -15,14 +15,26 @@ FactoryGirl.define do
     end
 
     trait :two_factor do
+      two_factor_via_otp
+    end
+
+    trait :two_factor_via_otp do
       before(:create) do |user|
-        user.two_factor_enabled = true
+        user.otp_required_for_login = true
         user.otp_secret = User.generate_otp_secret(32)
         user.otp_grace_period_started_at = Time.now
         user.generate_otp_backup_codes!
       end
     end
 
+    trait :two_factor_via_u2f do
+      transient { registrations_count 5 }
+
+      after(:create) do |user, evaluator|
+        create_list(:u2f_registration, evaluator.registrations_count, user: user)
+      end
+    end
+
     factory :omniauth_user do
       transient do
         extern_uid '123456'
diff --git a/spec/factories/wiki_pages.rb b/spec/factories/wiki_pages.rb
index 938ccf2306b975331311d1292f5796d0bc693efd..efa6cbe5bb1410f74348c65b0726d323d67fdbfb 100644
--- a/spec/factories/wiki_pages.rb
+++ b/spec/factories/wiki_pages.rb
@@ -2,7 +2,7 @@ require 'ostruct'
 
 FactoryGirl.define do
   factory :wiki_page do
-    page = OpenStruct.new(url_path: 'some-name')
+    page { OpenStruct.new(url_path: 'some-name') }
     association :wiki, factory: :project_wiki, strategy: :build
     initialize_with { new(wiki, page, true) }
   end
diff --git a/spec/factories_spec.rb b/spec/factories_spec.rb
index 62de081661d6510abc4bd5ba3842338b102c0bc0..675d9bd18b7aa9e1f538112e528e321c8ca5b641 100644
--- a/spec/factories_spec.rb
+++ b/spec/factories_spec.rb
@@ -5,8 +5,8 @@ describe 'factories' do
     describe "#{factory.name} factory" do
       let(:entity) { build(factory.name) }
 
-      it 'does not raise error when created 'do
-        expect { entity }.to_not raise_error
+      it 'does not raise error when created' do
+        expect { entity }.not_to raise_error
       end
 
       it 'should be valid', if: factory.build_class < ActiveRecord::Base do
diff --git a/spec/features/admin/admin_builds_spec.rb b/spec/features/admin/admin_builds_spec.rb
index 7bbe20fec43c4785fee4935ab8a85fc0f5abeb98..a6198389f04357f24d7f89d47352b6f78ad5e49a 100644
--- a/spec/features/admin/admin_builds_spec.rb
+++ b/spec/features/admin/admin_builds_spec.rb
@@ -6,15 +6,15 @@ describe 'Admin Builds' do
   end
 
   describe 'GET /admin/builds' do
-    let(:commit) { create(:ci_commit) }
+    let(:pipeline) { create(:ci_pipeline) }
 
     context 'All tab' do
       context 'when have builds' do
         it 'shows all builds' do
-          create(:ci_build, commit: commit, status: :pending)
-          create(:ci_build, commit: commit, status: :running)
-          create(:ci_build, commit: commit, status: :success)
-          create(:ci_build, commit: commit, status: :failed)
+          create(:ci_build, pipeline: pipeline, status: :pending)
+          create(:ci_build, pipeline: pipeline, status: :running)
+          create(:ci_build, pipeline: pipeline, status: :success)
+          create(:ci_build, pipeline: pipeline, status: :failed)
 
           visit admin_builds_path
 
@@ -39,9 +39,9 @@ describe 'Admin Builds' do
     context 'Running tab' do
       context 'when have running builds' do
         it 'shows running builds' do
-          build1 = create(:ci_build, commit: commit, status: :pending)
-          build2 = create(:ci_build, commit: commit, status: :success)
-          build3 = create(:ci_build, commit: commit, status: :failed)
+          build1 = create(:ci_build, pipeline: pipeline, status: :pending)
+          build2 = create(:ci_build, pipeline: pipeline, status: :success)
+          build3 = create(:ci_build, pipeline: pipeline, status: :failed)
 
           visit admin_builds_path(scope: :running)
 
@@ -55,7 +55,7 @@ describe 'Admin Builds' do
 
       context 'when have no builds running' do
         it 'shows a message' do
-          create(:ci_build, commit: commit, status: :success)
+          create(:ci_build, pipeline: pipeline, status: :success)
 
           visit admin_builds_path(scope: :running)
 
@@ -69,9 +69,9 @@ describe 'Admin Builds' do
     context 'Finished tab' do
       context 'when have finished builds' do
         it 'shows finished builds' do
-          build1 = create(:ci_build, commit: commit, status: :pending)
-          build2 = create(:ci_build, commit: commit, status: :running)
-          build3 = create(:ci_build, commit: commit, status: :success)
+          build1 = create(:ci_build, pipeline: pipeline, status: :pending)
+          build2 = create(:ci_build, pipeline: pipeline, status: :running)
+          build3 = create(:ci_build, pipeline: pipeline, status: :success)
 
           visit admin_builds_path(scope: :finished)
 
@@ -85,7 +85,7 @@ describe 'Admin Builds' do
 
       context 'when have no builds finished' do
         it 'shows a message' do
-          create(:ci_build, commit: commit, status: :running)
+          create(:ci_build, pipeline: pipeline, status: :running)
 
           visit admin_builds_path(scope: :finished)
 
diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb
index 7265cdac7a7323a2cc3b82b285b90b16ef63db66..31633817d534afaf43ca777c76ad2d05aad3c8cc 100644
--- a/spec/features/admin/admin_hooks_spec.rb
+++ b/spec/features/admin/admin_hooks_spec.rb
@@ -12,9 +12,11 @@ describe "Admin::Hooks", feature: true do
   describe "GET /admin/hooks" do
     it "should be ok" do
       visit admin_root_path
-      page.within ".sidebar-wrapper" do
+
+      page.within ".layout-nav" do
         click_on "Hooks"
       end
+
       expect(current_path).to eq(admin_hooks_path)
     end
 
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index 26d03944b8a24dd811e1a6da96f05b73118ae215..9499cd4e02529dba98feffbaf46322b524723c44 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -8,8 +8,8 @@ describe "Admin Runners" do
   describe "Runners page" do
     before do
       runner = FactoryGirl.create(:ci_runner)
-      commit = FactoryGirl.create(:ci_commit)
-      FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id)
+      pipeline = FactoryGirl.create(:ci_pipeline)
+      FactoryGirl.create(:ci_build, pipeline: pipeline, runner_id: runner.id)
       visit admin_runners_path
     end
 
@@ -79,7 +79,7 @@ describe "Admin Runners" do
       end
 
       it 'changes registration token' do
-        expect(page_token).to_not eq token
+        expect(page_token).not_to eq token
       end
     end
   end
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index 6dee0cd8d47d63c479255c1e7f791e358f86d19d..1cb709c1de30619935404e1a674c6045bcbfb685 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -19,7 +19,7 @@ describe "Admin::Users", feature: true  do
 
     describe 'Two-factor Authentication filters' do
       it 'counts users who have enabled 2FA' do
-        create(:user, two_factor_enabled: true)
+        create(:user, :two_factor)
 
         visit admin_users_path
 
@@ -29,7 +29,7 @@ describe "Admin::Users", feature: true  do
       end
 
       it 'filters by users who have enabled 2FA' do
-        user = create(:user, two_factor_enabled: true)
+        user = create(:user, :two_factor)
 
         visit admin_users_path
         click_link '2FA Enabled'
@@ -38,7 +38,7 @@ describe "Admin::Users", feature: true  do
       end
 
       it 'counts users who have not enabled 2FA' do
-        create(:user, two_factor_enabled: false)
+        create(:user)
 
         visit admin_users_path
 
@@ -48,7 +48,7 @@ describe "Admin::Users", feature: true  do
       end
 
       it 'filters by users who have not enabled 2FA' do
-        user = create(:user, two_factor_enabled: false)
+        user = create(:user)
 
         visit admin_users_path
         click_link '2FA Disabled'
@@ -144,22 +144,22 @@ describe "Admin::Users", feature: true  do
         before { click_link 'Impersonate' }
 
         it 'logs in as the user when impersonate is clicked' do
-          page.within '.sidebar-user .username' do
-            expect(page).to have_content(another_user.username)
+          page.within '.sidebar-wrapper' do
+            expect(page.find('.sidebar-user')['data-user']).to eql(another_user.username)
           end
         end
 
         it 'sees impersonation log out icon' do
           icon = first('.fa.fa-user-secret')
 
-          expect(icon).to_not eql nil
+          expect(icon).not_to eql nil
         end
 
         it 'can log out of impersonated user back to original user' do
           find(:css, 'li.impersonation a').click
 
-          page.within '.sidebar-user .username' do
-            expect(page).to have_content(@user.username)
+          page.within '.sidebar-wrapper' do
+            expect(page.find('.sidebar-user')['data-user']).to eql(@user.username)
           end
         end
 
@@ -173,7 +173,7 @@ describe "Admin::Users", feature: true  do
 
     describe 'Two-factor Authentication status' do
       it 'shows when enabled' do
-        @user.update_attribute(:two_factor_enabled, true)
+        @user.update_attribute(:otp_required_for_login, true)
 
         visit admin_user_path(@user)
 
diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb
index b710cb3c72ff33b85d85de20b73bb0d9e954e48f..4dd9548cfc51737649d28629e63851f771c4f7d7 100644
--- a/spec/features/atom/dashboard_issues_spec.rb
+++ b/spec/features/atom/dashboard_issues_spec.rb
@@ -5,8 +5,6 @@ describe "Dashboard Issues Feed", feature: true  do
     let!(:user)     { create(:user) }
     let!(:project1) { create(:project) }
     let!(:project2) { create(:project) }
-    let!(:issue1)   { create(:issue, author: user, assignee: user, project: project1) }
-    let!(:issue2)   { create(:issue, author: user, assignee: user, project: project2) }
 
     before do
       project1.team << [user, :master]
@@ -14,16 +12,51 @@ describe "Dashboard Issues Feed", feature: true  do
     end
 
     describe "atom feed" do
-      it "should render atom feed via private token" do
+      it "renders atom feed via private token" do
         visit issues_dashboard_path(:atom, private_token: user.private_token)
 
-        expect(response_headers['Content-Type']).
-          to have_content('application/atom+xml')
+        expect(response_headers['Content-Type']).to have_content('application/atom+xml')
         expect(body).to have_selector('title', text: "#{user.name} issues")
-        expect(body).to have_selector('author email', text: issue1.author_email)
-        expect(body).to have_selector('entry summary', text: issue1.title)
-        expect(body).to have_selector('author email', text: issue2.author_email)
-        expect(body).to have_selector('entry summary', text: issue2.title)
+      end
+
+      context "issue with basic fields" do
+        let!(:issue2) { create(:issue, author: user, assignee: user, project: project2, description: 'test desc') }
+
+        it "renders issue fields" do
+          visit issues_dashboard_path(:atom, private_token: user.private_token)
+
+          entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue2.title}')]")
+
+          expect(entry).to be_present
+          expect(entry).to have_selector('author email', text: issue2.author_email)
+          expect(entry).to have_selector('assignee email', text: issue2.author_email)
+          expect(entry).not_to have_selector('labels')
+          expect(entry).not_to have_selector('milestone')
+          expect(entry).to have_selector('description', text: issue2.description)
+        end
+      end
+
+      context "issue with label and milestone" do
+        let!(:milestone1) { create(:milestone, project: project1, title: 'v1') }
+        let!(:label1)     { create(:label, project: project1, title: 'label1') }
+        let!(:issue1)     { create(:issue, author: user, assignee: user, project: project1, milestone: milestone1) }
+
+        before do
+          issue1.labels << label1
+        end
+
+        it "renders issue label and milestone info" do
+          visit issues_dashboard_path(:atom, private_token: user.private_token)
+
+          entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue1.title}')]")
+
+          expect(entry).to be_present
+          expect(entry).to have_selector('author email', text: issue1.author_email)
+          expect(entry).to have_selector('assignee email', text: issue1.author_email)
+          expect(entry).to have_selector('labels label', text: label1.title)
+          expect(entry).to have_selector('milestone', text: milestone1.title)
+          expect(entry).not_to have_selector('description')
+        end
       end
     end
   end
diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb
index f83a78308e37555c8a625c44e60876518faf5b26..16832c297acd6c14d7d53d0406c3d29af2d3219c 100644
--- a/spec/features/builds_spec.rb
+++ b/spec/features/builds_spec.rb
@@ -5,8 +5,9 @@ describe "Builds" do
 
   before do
     login_as(:user)
-    @commit = FactoryGirl.create :ci_commit
-    @build = FactoryGirl.create :ci_build, commit: @commit
+    @commit = FactoryGirl.create :ci_pipeline
+    @build = FactoryGirl.create :ci_build, pipeline: @commit
+    @build2 = FactoryGirl.create :ci_build
     @project = @commit.project
     @project.team << [@user, :developer]
   end
@@ -43,11 +44,10 @@ describe "Builds" do
       end
 
       it { expect(page).to have_selector('.nav-links li.active', text: 'All') }
-      it { expect(page).to have_selector('.row-content-block', text: 'All builds from this project') }
       it { expect(page).to have_content @build.short_sha }
       it { expect(page).to have_content @build.ref }
       it { expect(page).to have_content @build.name }
-      it { expect(page).to_not have_link 'Cancel running' }
+      it { expect(page).not_to have_link 'Cancel running' }
     end
   end
 
@@ -63,17 +63,28 @@ describe "Builds" do
     it { expect(page).to have_content @build.short_sha }
     it { expect(page).to have_content @build.ref }
     it { expect(page).to have_content @build.name }
-    it { expect(page).to_not have_link 'Cancel running' }
+    it { expect(page).not_to have_link 'Cancel running' }
   end
 
   describe "GET /:project/builds/:id" do
-    before do
-      visit namespace_project_build_path(@project.namespace, @project, @build)
+    context "Build from project" do
+      before do
+        visit namespace_project_build_path(@project.namespace, @project, @build)
+      end
+
+      it { expect(page.status_code).to eq(200) }
+      it { expect(page).to have_content @commit.sha[0..7] }
+      it { expect(page).to have_content @commit.git_commit_message }
+      it { expect(page).to have_content @commit.git_author_name }
     end
 
-    it { expect(page).to have_content @commit.sha[0..7] }
-    it { expect(page).to have_content @commit.git_commit_message }
-    it { expect(page).to have_content @commit.git_author_name }
+    context "Build from other project" do
+      before do
+        visit namespace_project_build_path(@project.namespace, @project, @build2)
+      end
+
+      it { expect(page.status_code).to eq(404) }
+    end
 
     context "Download artifacts" do
       before do
@@ -82,8 +93,42 @@ describe "Builds" do
       end
 
       it 'has button to download artifacts' do
-        page.within('.artifacts') do
-          expect(page).to have_content 'Download'
+        expect(page).to have_content 'Download'
+      end
+    end
+
+    context 'Artifacts expire date' do
+      before do
+        @build.update_attributes(artifacts_file: artifacts_file, artifacts_expire_at: expire_at)
+        visit namespace_project_build_path(@project.namespace, @project, @build)
+      end
+
+      context 'no expire date defined' do
+        let(:expire_at) { nil }
+
+        it 'does not have the Keep button' do
+          expect(page).not_to have_content 'Keep'
+        end
+      end
+
+      context 'when expire date is defined' do
+        let(:expire_at) { Time.now + 7.days }
+
+        it 'keeps artifacts when Keep button is clicked' do
+          expect(page).to have_content 'The artifacts will be removed'
+          click_link 'Keep'
+
+          expect(page).not_to have_link 'Keep'
+          expect(page).not_to have_content 'The artifacts will be removed'
+        end
+      end
+
+      context 'when artifacts expired' do
+        let(:expire_at) { Time.now - 7.days }
+
+        it 'does not have the Keep button' do
+          expect(page).to have_content 'The artifacts were removed'
+          expect(page).not_to have_link 'Keep'
         end
       end
     end
@@ -96,59 +141,144 @@ describe "Builds" do
       end
 
       it do
-        page.within('.build-controls') do
-          expect(page).to have_link 'Raw'
-        end
+        expect(page).to have_link 'Raw'
       end
     end
   end
 
   describe "POST /:project/builds/:id/cancel" do
-    before do
-      @build.run!
-      visit namespace_project_build_path(@project.namespace, @project, @build)
-      click_link "Cancel"
+    context "Build from project" do
+      before do
+        @build.run!
+        visit namespace_project_build_path(@project.namespace, @project, @build)
+        click_link "Cancel"
+      end
+
+      it { expect(page.status_code).to eq(200) }
+      it { expect(page).to have_content 'canceled' }
+      it { expect(page).to have_content 'Retry' }
     end
 
-    it { expect(page).to have_content 'canceled' }
-    it { expect(page).to have_content 'Retry' }
+    context "Build from other project" do
+      before do
+        @build.run!
+        visit namespace_project_build_path(@project.namespace, @project, @build)
+        page.driver.post(cancel_namespace_project_build_path(@project.namespace, @project, @build2))
+      end
+
+      it { expect(page.status_code).to eq(404) }
+    end
   end
 
   describe "POST /:project/builds/:id/retry" do
-    before do
-      @build.run!
-      visit namespace_project_build_path(@project.namespace, @project, @build)
-      click_link "Cancel"
-      click_link 'Retry'
+    context "Build from project" do
+      before do
+        @build.run!
+        visit namespace_project_build_path(@project.namespace, @project, @build)
+        click_link 'Cancel'
+        click_link 'Retry'
+      end
+
+      it { expect(page.status_code).to eq(200) }
+      it { expect(page).to have_content 'pending' }
+      it { expect(page).to have_content 'Cancel' }
     end
 
-    it { expect(page).to have_content 'pending' }
-    it { expect(page).to have_content 'Cancel' }
+    context "Build from other project" do
+      before do
+        @build.run!
+        visit namespace_project_build_path(@project.namespace, @project, @build)
+        click_link 'Cancel'
+        page.driver.post(retry_namespace_project_build_path(@project.namespace, @project, @build2))
+      end
+
+      it { expect(page.status_code).to eq(404) }
+    end
   end
 
   describe "GET /:project/builds/:id/download" do
     before do
       @build.update_attributes(artifacts_file: artifacts_file)
       visit namespace_project_build_path(@project.namespace, @project, @build)
-      page.within('.artifacts') { click_link 'Download' }
+      click_link 'Download'
     end
 
-    it { expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type) }
+    context "Build from other project" do
+      before do
+        @build2.update_attributes(artifacts_file: artifacts_file)
+        visit download_namespace_project_build_artifacts_path(@project.namespace, @project, @build2)
+      end
+
+      it { expect(page.status_code).to eq(404) }
+    end
   end
 
   describe "GET /:project/builds/:id/raw" do
-    before do
-      Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile')
-      @build.run!
-      @build.trace = 'BUILD TRACE'
-      visit namespace_project_build_path(@project.namespace, @project, @build)
+    context "Build from project" do
+      before do
+        Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile')
+        @build.run!
+        @build.trace = 'BUILD TRACE'
+        visit namespace_project_build_path(@project.namespace, @project, @build)
+        page.within('.js-build-sidebar') { click_link 'Raw' }
+      end
+
+      it 'sends the right headers' do
+        expect(page.status_code).to eq(200)
+        expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8')
+        expect(page.response_headers['X-Sendfile']).to eq(@build.path_to_trace)
+      end
+    end
+
+    context "Build from other project" do
+      before do
+        Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile')
+        @build2.run!
+        @build2.trace = 'BUILD TRACE'
+        visit raw_namespace_project_build_path(@project.namespace, @project, @build2)
+        puts page.status_code
+        puts current_url
+      end
+
+      it 'sends the right headers' do
+        expect(page.status_code).to eq(404)
+      end
+    end
+  end
+
+  describe "GET /:project/builds/:id/trace.json" do
+    context "Build from project" do
+      before do
+        visit trace_namespace_project_build_path(@project.namespace, @project, @build, format: :json)
+      end
+
+      it { expect(page.status_code).to eq(200) }
+    end
+
+    context "Build from other project" do
+      before do
+        visit trace_namespace_project_build_path(@project.namespace, @project, @build2, format: :json)
+      end
+
+      it { expect(page.status_code).to eq(404) }
+    end
+  end
+
+  describe "GET /:project/builds/:id/status" do
+    context "Build from project" do
+      before do
+        visit status_namespace_project_build_path(@project.namespace, @project, @build)
+      end
+
+      it { expect(page.status_code).to eq(200) }
     end
 
-    it 'sends the right headers' do
-      page.within('.build-controls') { click_link 'Raw' }
+    context "Build from other project" do
+      before do
+        visit status_namespace_project_build_path(@project.namespace, @project, @build2)
+      end
 
-      expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8')
-      expect(page.response_headers['X-Sendfile']).to eq(@build.path_to_trace)
+      it { expect(page.status_code).to eq(404) }
     end
   end
 end
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index dacaa96d76046be29bdb43ada3c29f5d757a92c2..45e1a157a1f1bca31e79769d162695050e67d8b3 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -8,15 +8,15 @@ describe 'Commits' do
   describe 'CI' do
     before do
       login_as :user
-      stub_ci_commit_to_return_yaml_file
+      stub_ci_pipeline_to_return_yaml_file
     end
 
-    let!(:commit) do
-      FactoryGirl.create :ci_commit, project: project, sha: project.commit.sha
+    let!(:pipeline) do
+      FactoryGirl.create :ci_pipeline, project: project, sha: project.commit.sha
     end
 
     context 'commit status is Generic Commit Status' do
-      let!(:status) { FactoryGirl.create :generic_commit_status, commit: commit }
+      let!(:status) { FactoryGirl.create :generic_commit_status, pipeline: pipeline }
 
       before do
         project.team << [@user, :reporter]
@@ -24,10 +24,10 @@ describe 'Commits' do
 
       describe 'Commit builds' do
         before do
-          visit ci_status_path(commit)
+          visit ci_status_path(pipeline)
         end
 
-        it { expect(page).to have_content commit.sha[0..7] }
+        it { expect(page).to have_content pipeline.sha[0..7] }
 
         it 'contains generic commit status build' do
           page.within('.table-holder') do
@@ -39,7 +39,7 @@ describe 'Commits' do
     end
 
     context 'commit status is Ci Build' do
-      let!(:build) { FactoryGirl.create :ci_build, commit: commit }
+      let!(:build) { FactoryGirl.create :ci_build, pipeline: pipeline }
       let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
 
       context 'when logged as developer' do
@@ -53,7 +53,7 @@ describe 'Commits' do
           end
 
           it 'should show build status' do
-            page.within("//li[@id='commit-#{commit.short_sha}']") do
+            page.within("//li[@id='commit-#{pipeline.short_sha}']") do
               expect(page).to have_css(".ci-status-link")
             end
           end
@@ -61,12 +61,12 @@ describe 'Commits' do
 
         describe 'Commit builds' do
           before do
-            visit ci_status_path(commit)
+            visit ci_status_path(pipeline)
           end
 
-          it { expect(page).to have_content commit.sha[0..7] }
-          it { expect(page).to have_content commit.git_commit_message }
-          it { expect(page).to have_content commit.git_author_name }
+          it { expect(page).to have_content pipeline.sha[0..7] }
+          it { expect(page).to have_content pipeline.git_commit_message }
+          it { expect(page).to have_content pipeline.git_author_name }
         end
 
         context 'Download artifacts' do
@@ -75,7 +75,7 @@ describe 'Commits' do
           end
 
           it do
-            visit ci_status_path(commit)
+            visit ci_status_path(pipeline)
             click_on 'Download artifacts'
             expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type)
           end
@@ -83,7 +83,7 @@ describe 'Commits' do
 
         describe 'Cancel all builds' do
           it 'cancels commit' do
-            visit ci_status_path(commit)
+            visit ci_status_path(pipeline)
             click_on 'Cancel running'
             expect(page).to have_content 'canceled'
           end
@@ -91,7 +91,7 @@ describe 'Commits' do
 
         describe 'Cancel build' do
           it 'cancels build' do
-            visit ci_status_path(commit)
+            visit ci_status_path(pipeline)
             click_on 'Cancel'
             expect(page).to have_content 'canceled'
           end
@@ -100,13 +100,13 @@ describe 'Commits' do
         describe '.gitlab-ci.yml not found warning' do
           context 'ci builds enabled' do
             it "does not show warning" do
-              visit ci_status_path(commit)
+              visit ci_status_path(pipeline)
               expect(page).not_to have_content '.gitlab-ci.yml not found in this commit'
             end
 
             it 'shows warning' do
-              stub_ci_commit_yaml_file(nil)
-              visit ci_status_path(commit)
+              stub_ci_pipeline_yaml_file(nil)
+              visit ci_status_path(pipeline)
               expect(page).to have_content '.gitlab-ci.yml not found in this commit'
             end
           end
@@ -114,8 +114,8 @@ describe 'Commits' do
           context 'ci builds disabled' do
             before do
               stub_ci_builds_disabled
-              stub_ci_commit_yaml_file(nil)
-              visit ci_status_path(commit)
+              stub_ci_pipeline_yaml_file(nil)
+              visit ci_status_path(pipeline)
             end
 
             it 'does not show warning' do
@@ -129,16 +129,16 @@ describe 'Commits' do
         before do
           project.team << [@user, :reporter]
           build.update_attributes(artifacts_file: artifacts_file)
-          visit ci_status_path(commit)
+          visit ci_status_path(pipeline)
         end
 
         it do
-          expect(page).to have_content commit.sha[0..7]
-          expect(page).to have_content commit.git_commit_message
-          expect(page).to have_content commit.git_author_name
+          expect(page).to have_content pipeline.sha[0..7]
+          expect(page).to have_content pipeline.git_commit_message
+          expect(page).to have_content pipeline.git_author_name
           expect(page).to have_link('Download artifacts')
-          expect(page).to_not have_link('Cancel running')
-          expect(page).to_not have_link('Retry failed')
+          expect(page).not_to have_link('Cancel running')
+          expect(page).not_to have_link('Retry failed')
         end
       end
 
@@ -148,16 +148,16 @@ describe 'Commits' do
             visibility_level: Gitlab::VisibilityLevel::INTERNAL,
             public_builds: false)
           build.update_attributes(artifacts_file: artifacts_file)
-          visit ci_status_path(commit)
+          visit ci_status_path(pipeline)
         end
 
         it do
-          expect(page).to have_content commit.sha[0..7]
-          expect(page).to have_content commit.git_commit_message
-          expect(page).to have_content commit.git_author_name
-          expect(page).to_not have_link('Download artifacts')
-          expect(page).to_not have_link('Cancel running')
-          expect(page).to_not have_link('Retry failed')
+          expect(page).to have_content pipeline.sha[0..7]
+          expect(page).to have_content pipeline.git_commit_message
+          expect(page).to have_content pipeline.git_author_name
+          expect(page).not_to have_link('Download artifacts')
+          expect(page).not_to have_link('Cancel running')
+          expect(page).not_to have_link('Retry failed')
         end
       end
     end
diff --git a/spec/features/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..365cb445df1e519d634501a2de2c1c27897bae40
--- /dev/null
+++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+feature 'Tooltips on .timeago dates', feature: true, js: true do
+  include WaitForAjax
+
+  let(:user)            { create(:user) }
+  let(:project)         { create(:project, name: 'test', namespace: user.namespace) }
+  let(:created_date)    { Date.yesterday.to_time }
+  let(:expected_format) { created_date.strftime('%b %-d, %Y %l:%M%P UTC') }
+
+  context 'on the activity tab' do
+    before do
+      project.team << [user, :master]
+
+      Event.create( project: project, author_id: user.id, action: Event::JOINED,
+                    updated_at: created_date, created_at: created_date)
+
+      login_as user
+      visit user_path(user)
+      wait_for_ajax()
+
+      page.find('.js-timeago').hover
+    end
+
+    it 'has the datetime formated correctly' do
+      expect(page).to have_selector('.local-timeago', text: expected_format)
+    end
+  end
+
+  context 'on the snippets tab' do
+    before do
+      project.team << [user, :master]
+      create(:snippet, author: user, updated_at: created_date, created_at: created_date)
+
+      login_as user
+      visit user_snippets_path(user)
+      wait_for_ajax()
+
+      page.find('.js-timeago').hover
+    end
+
+    it 'has the datetime formated correctly' do
+      expect(page).to have_selector('.local-timeago', text: expected_format)
+    end
+  end
+end
diff --git a/spec/features/environments_spec.rb b/spec/features/environments_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..40fea5211e966afac45df721ae01f56e2d51d4fd
--- /dev/null
+++ b/spec/features/environments_spec.rb
@@ -0,0 +1,160 @@
+require 'spec_helper'
+
+feature 'Environments', feature: true do
+  given(:project) { create(:empty_project) }
+  given(:user) { create(:user) }
+  given(:role) { :developer }
+
+  background do
+    login_as(user)
+    project.team << [user, role]
+  end
+
+  describe 'when showing environments' do
+    given!(:environment) { }
+    given!(:deployment) { }
+
+    before do
+      visit namespace_project_environments_path(project.namespace, project)
+    end
+
+    context 'without environments' do
+      scenario 'does show no environments' do
+        expect(page).to have_content('No environments to show')
+      end
+    end
+
+    context 'with environments' do
+      given(:environment) { create(:environment, project: project) }
+
+      scenario 'does show environment name' do
+        expect(page).to have_link(environment.name)
+      end
+
+      context 'without deployments' do
+        scenario 'does show no deployments' do
+          expect(page).to have_content('No deployments yet')
+        end
+      end
+
+      context 'with deployments' do
+        given(:deployment) { create(:deployment, environment: environment) }
+
+        scenario 'does show deployment SHA' do
+          expect(page).to have_link(deployment.short_sha)
+        end
+      end
+    end
+
+    scenario 'does have a New environment button' do
+      expect(page).to have_link('New environment')
+    end
+  end
+
+  describe 'when showing the environment' do
+    given(:environment) { create(:environment, project: project) }
+    given!(:deployment) { }
+
+    before do
+      visit namespace_project_environment_path(project.namespace, project, environment)
+    end
+
+    context 'without deployments' do
+      scenario 'does show no deployments' do
+        expect(page).to have_content('No deployments for')
+      end
+    end
+
+    context 'with deployments' do
+      given(:deployment) { create(:deployment, environment: environment) }
+
+      scenario 'does show deployment SHA' do
+        expect(page).to have_link(deployment.short_sha)
+      end
+
+      scenario 'does not show a retry button for deployment without build' do
+        expect(page).not_to have_link('Retry')
+      end
+
+      context 'with build' do
+        given(:build) { create(:ci_build, project: project) }
+        given(:deployment) { create(:deployment, environment: environment, deployable: build) }
+
+        scenario 'does show build name' do
+          expect(page).to have_link("#{build.name} (##{build.id})")
+        end
+
+        scenario 'does show retry button' do
+          expect(page).to have_link('Retry')
+        end
+      end
+    end
+  end
+
+  describe 'when creating a new environment' do
+    before do
+      visit namespace_project_environments_path(project.namespace, project)
+    end
+
+    context 'when logged as developer' do
+      before do
+        click_link 'New environment'
+      end
+
+      context 'for valid name' do
+        before do
+          fill_in('Name', with: 'production')
+          click_on 'Create environment'
+        end
+
+        scenario 'does create a new pipeline' do
+          expect(page).to have_content('production')
+        end
+      end
+
+      context 'for invalid name' do
+        before do
+          fill_in('Name', with: 'name with spaces')
+          click_on 'Create environment'
+        end
+
+        scenario 'does show errors' do
+          expect(page).to have_content('Name can contain only letters')
+        end
+      end
+    end
+
+    context 'when logged as reporter' do
+      given(:role) { :reporter }
+
+      scenario 'does not have a New environment link' do
+        expect(page).not_to have_link('New environment')
+      end
+    end
+  end
+
+  describe 'when deleting existing environment' do
+    given(:environment) { create(:environment, project: project) }
+
+    before do
+      visit namespace_project_environment_path(project.namespace, project, environment)
+    end
+
+    context 'when logged as master' do
+      given(:role) { :master }
+
+      scenario 'does delete environment' do
+        click_link 'Destroy'
+        expect(page).not_to have_link(environment.name)
+      end
+    end
+
+    context 'when logged as developer' do
+      given(:role) { :developer }
+
+      scenario 'does not have a Destroy link' do
+        expect(page).not_to have_link('Destroy')
+      end
+    end
+  end
+end
diff --git a/spec/features/groups/members/owner_manages_access_requests_spec.rb b/spec/features/groups/members/owner_manages_access_requests_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..22525ce530b1522dd70ab8b94a857c2b2e593124
--- /dev/null
+++ b/spec/features/groups/members/owner_manages_access_requests_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+feature 'Groups > Members > Owner manages access requests', feature: true do
+  let(:user) { create(:user) }
+  let(:owner) { create(:user) }
+  let(:group) { create(:group, :public) }
+
+  background do
+    group.request_access(user)
+    group.add_owner(owner)
+    login_as(owner)
+  end
+
+  scenario 'owner can see access requests' do
+    visit group_group_members_path(group)
+
+    expect_visible_access_request(group, user)
+  end
+
+  scenario 'master can grant access' do
+    visit group_group_members_path(group)
+
+    expect_visible_access_request(group, user)
+
+    perform_enqueued_jobs { click_on 'Grant access' }
+
+    expect(ActionMailer::Base.deliveries.last.to).to eq [user.notification_email]
+    expect(ActionMailer::Base.deliveries.last.subject).to match "Access to the #{group.name} group was granted"
+  end
+
+  scenario 'master can deny access' do
+    visit group_group_members_path(group)
+
+    expect_visible_access_request(group, user)
+
+    perform_enqueued_jobs { click_on 'Deny access' }
+
+    expect(ActionMailer::Base.deliveries.last.to).to eq [user.notification_email]
+    expect(ActionMailer::Base.deliveries.last.subject).to match "Access to the #{group.name} group was denied"
+  end
+
+
+  def expect_visible_access_request(group, user)
+    expect(group.members.request.exists?(user_id: user)).to be_truthy
+    expect(page).to have_content "#{group.name} access requests (1)"
+    expect(page).to have_content user.name
+  end
+end
diff --git a/spec/features/groups/members/user_requests_access_spec.rb b/spec/features/groups/members/user_requests_access_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a878a96b6ee398e62710d8eddfd1ebd3f1c9c44f
--- /dev/null
+++ b/spec/features/groups/members/user_requests_access_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+feature 'Groups > Members > User requests access', feature: true do
+  let(:user) { create(:user) }
+  let(:owner) { create(:user) }
+  let(:group) { create(:group, :public) }
+
+  background do
+    group.add_owner(owner)
+    login_as(user)
+    visit group_path(group)
+  end
+
+  scenario 'user can request access to a group' do
+    perform_enqueued_jobs { click_link 'Request Access' }
+
+    expect(ActionMailer::Base.deliveries.last.to).to eq [owner.notification_email]
+    expect(ActionMailer::Base.deliveries.last.subject).to match "Request to join the #{group.name} group"
+
+    expect(group.members.request.exists?(user_id: user)).to be_truthy
+    expect(page).to have_content 'Your request for access has been queued for review.'
+
+    expect(page).to have_content 'Withdraw Access Request'
+  end
+
+  scenario 'user is not listed in the group members page' do
+    click_link 'Request Access'
+
+    expect(group.members.request.exists?(user_id: user)).to be_truthy
+
+    click_link 'Members'
+
+    page.within('.content') do
+      expect(page).not_to have_content(user.name)
+    end
+  end
+
+  scenario 'user can withdraw its request for access' do
+    click_link 'Request Access'
+
+    expect(group.members.request.exists?(user_id: user)).to be_truthy
+
+    click_link 'Withdraw Access Request'
+
+    expect(group.members.request.exists?(user_id: user)).to be_falsey
+    expect(page).to have_content 'Your access request to the group has been withdrawn.'
+  end
+end
diff --git a/spec/features/issues/award_emoji_spec.rb b/spec/features/issues/award_emoji_spec.rb
index 41af789aae273137dd6c4bdb2ca8377e5393d6e3..07a854ea01419177d76cb7664c2103f2a47a2846 100644
--- a/spec/features/issues/award_emoji_spec.rb
+++ b/spec/features/issues/award_emoji_spec.rb
@@ -28,7 +28,6 @@ describe 'Awards Emoji', feature: true do
     end
 
     context 'click the thumbsup emoji' do
-
       it 'should increment the thumbsup emoji', js: true do
         find('[data-emoji="thumbsup"]').click
         sleep 2
@@ -41,7 +40,6 @@ describe 'Awards Emoji', feature: true do
     end
 
     context 'click the thumbsdown emoji' do
-
       it 'should increment the thumbsdown emoji', js: true do
         find('[data-emoji="thumbsdown"]').click
         sleep 2
diff --git a/spec/features/issues/award_spec.rb b/spec/features/issues/award_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..63efecf87802bd842aea91ae8e35fc62a007d11d
--- /dev/null
+++ b/spec/features/issues/award_spec.rb
@@ -0,0 +1,49 @@
+require 'rails_helper'
+
+feature 'Issue awards', js: true, feature: true do
+  let(:user)      { create(:user) }
+  let(:project)   { create(:project, :public) }
+  let(:issue)     { create(:issue, project: project) }
+
+  describe 'logged in' do
+    before do
+      login_as(user)
+      visit namespace_project_issue_path(project.namespace, project, issue)
+    end
+
+    it 'should add award to issue' do
+      first('.js-emoji-btn').click
+      expect(page).to have_selector('.js-emoji-btn.active')
+      expect(first('.js-emoji-btn')).to have_content '1'
+
+      visit namespace_project_issue_path(project.namespace, project, issue)
+      expect(first('.js-emoji-btn')).to have_content '1'
+    end
+
+    it 'should remove award from issue' do
+      first('.js-emoji-btn').click
+      find('.js-emoji-btn.active').click
+      expect(first('.js-emoji-btn')).to have_content '0'
+
+      visit namespace_project_issue_path(project.namespace, project, issue)
+      expect(first('.js-emoji-btn')).to have_content '0'
+    end
+
+    it 'should only have one menu on the page' do
+      first('.js-add-award').click
+      expect(page).to have_selector('.emoji-menu')
+
+      expect(page).to have_selector('.emoji-menu', count: 1)
+    end
+  end
+
+  describe 'logged out' do
+    before do
+      visit namespace_project_issue_path(project.namespace, project, issue)
+    end
+
+    it 'should not see award menu button' do
+      expect(page).not_to have_selector('.js-award-holder')
+    end
+  end
+end
diff --git a/spec/features/issues/bulk_assigment_labels_spec.rb b/spec/features/issues/bulk_assigment_labels_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0fbc2062e39edd01dc328742ba66ecc4770b6c72
--- /dev/null
+++ b/spec/features/issues/bulk_assigment_labels_spec.rb
@@ -0,0 +1,213 @@
+require 'rails_helper'
+
+feature 'Issues > Labels bulk assignment', feature: true do
+  include WaitForAjax
+
+  let(:user)      { create(:user) }
+  let!(:project)  { create(:project) }
+  let!(:issue1)   { create(:issue, project: project, title: "Issue 1") }
+  let!(:issue2)   { create(:issue, project: project, title: "Issue 2") }
+  let!(:bug)      { create(:label, project: project, title: 'bug') }
+  let!(:feature)  { create(:label, project: project, title: 'feature') }
+
+  context 'as a allowed user', js: true do
+    before do
+      project.team << [user, :master]
+
+      login_as user
+    end
+
+    context 'can bulk assign' do
+      before do
+        visit namespace_project_issues_path(project.namespace, project)
+      end
+
+      context 'a label' do
+        context 'to all issues' do
+          before do
+            check 'check_all_issues'
+            open_labels_dropdown ['bug']
+            update_issues
+          end
+
+          it do
+            expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+            expect(find("#issue_#{issue2.id}")).to have_content 'bug'
+          end
+        end
+
+        context 'to a issue' do
+          before do
+            check "selected_issue_#{issue1.id}"
+            open_labels_dropdown ['bug']
+            update_issues
+          end
+
+          it do
+            expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+            expect(find("#issue_#{issue2.id}")).not_to have_content 'bug'
+          end
+        end
+      end
+
+      context 'multiple labels' do
+        context 'to all issues' do
+          before do
+            check 'check_all_issues'
+            open_labels_dropdown ['bug', 'feature']
+            update_issues
+          end
+
+          it do
+            expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+            expect(find("#issue_#{issue1.id}")).to have_content 'feature'
+            expect(find("#issue_#{issue2.id}")).to have_content 'bug'
+            expect(find("#issue_#{issue2.id}")).to have_content 'feature'
+          end
+        end
+
+        context 'to a issue' do
+          before do
+            check "selected_issue_#{issue1.id}"
+            open_labels_dropdown ['bug', 'feature']
+            update_issues
+          end
+
+          it do
+            expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+            expect(find("#issue_#{issue1.id}")).to have_content 'feature'
+            expect(find("#issue_#{issue2.id}")).not_to have_content 'bug'
+            expect(find("#issue_#{issue2.id}")).not_to have_content 'feature'
+          end
+        end
+      end
+    end
+
+    context 'can assign a label to all issues when label is present' do
+      before do
+        issue2.labels << bug
+        issue2.labels << feature
+        visit namespace_project_issues_path(project.namespace, project)
+
+        check 'check_all_issues'
+        open_labels_dropdown ['bug']
+        update_issues
+      end
+
+      it do
+        expect(find("#issue_#{issue1.id}")).to have_content 'bug'
+        expect(find("#issue_#{issue2.id}")).to have_content 'bug'
+      end
+    end
+
+    context 'can bulk un-assign' do
+      context 'all labels to all issues' do
+        before do
+          issue1.labels << bug
+          issue1.labels << feature
+          issue2.labels << bug
+          issue2.labels << feature
+
+          visit namespace_project_issues_path(project.namespace, project)
+
+          check 'check_all_issues'
+          unmark_labels_in_dropdown ['bug', 'feature']
+          update_issues
+        end
+
+        it do
+          expect(find("#issue_#{issue1.id}")).not_to have_content 'bug'
+          expect(find("#issue_#{issue1.id}")).not_to have_content 'feature'
+          expect(find("#issue_#{issue2.id}")).not_to have_content 'bug'
+          expect(find("#issue_#{issue2.id}")).not_to have_content 'feature'
+        end
+      end
+
+      context 'a label to a issue' do
+        before do
+          issue1.labels << bug
+          issue2.labels << feature
+
+          visit namespace_project_issues_path(project.namespace, project)
+
+          check_issue issue1
+          unmark_labels_in_dropdown ['bug']
+          update_issues
+        end
+
+        it do
+          expect(find("#issue_#{issue1.id}")).not_to have_content 'bug'
+          expect(find("#issue_#{issue2.id}")).to have_content 'feature'
+        end
+      end
+
+      context 'a label and keep the others label' do
+        before do
+          issue1.labels << bug
+          issue1.labels << feature
+          issue2.labels << bug
+          issue2.labels << feature
+
+          visit namespace_project_issues_path(project.namespace, project)
+
+          check_issue issue1
+          check_issue issue2
+          unmark_labels_in_dropdown ['bug']
+          update_issues
+        end
+
+        it do
+          expect(find("#issue_#{issue1.id}")).not_to have_content 'bug'
+          expect(find("#issue_#{issue1.id}")).to have_content 'feature'
+          expect(find("#issue_#{issue2.id}")).not_to have_content 'bug'
+          expect(find("#issue_#{issue2.id}")).to have_content 'feature'
+        end
+      end
+    end
+  end
+
+  context 'as a guest' do
+    before do
+      login_as user
+
+      visit namespace_project_issues_path(project.namespace, project)
+    end
+
+    context 'cannot bulk assign labels' do
+      it do
+        expect(page).not_to have_css '.check_all_issues'
+        expect(page).not_to have_css '.issue-check'
+      end
+    end
+  end
+
+  def open_labels_dropdown(items = [], unmark = false)
+    page.within('.issues_bulk_update') do
+      click_button 'Label'
+      wait_for_ajax
+      items.map do |item|
+        click_link item
+      end
+      if unmark
+        items.map do |item|
+          click_link item
+        end
+      end
+    end
+  end
+
+  def unmark_labels_in_dropdown(items = [])
+    open_labels_dropdown(items, true)
+  end
+
+  def check_issue(issue)
+    page.within('.issues-list') do
+      check "selected_issue_#{issue.id}"
+    end
+  end
+
+  def update_issues
+    click_button 'Update issues'
+    wait_for_ajax
+  end
+end
diff --git a/spec/features/issues/filter_by_labels_spec.rb b/spec/features/issues/filter_by_labels_spec.rb
index 7f654684143934dffa6851e0af98ddfce2004315..5ea02b8d39c4d4adc0880e044eaffc4f31f0d27c 100644
--- a/spec/features/issues/filter_by_labels_spec.rb
+++ b/spec/features/issues/filter_by_labels_spec.rb
@@ -54,6 +54,12 @@ feature 'Issue filtering by Labels', feature: true do
       expect(find('.filtered-labels')).not_to have_content "feature"
       expect(find('.filtered-labels')).not_to have_content "enhancement"
     end
+
+    it 'should remove label "bug"' do
+      find('.js-label-filter-remove').click
+      wait_for_ajax
+      expect(find('.filtered-labels', visible: false)).to have_no_content "bug"
+    end
   end
 
   context 'filter by label feature', js: true do
@@ -135,6 +141,12 @@ feature 'Issue filtering by Labels', feature: true do
     it 'should not show label "bug" in filtered-labels' do
       expect(find('.filtered-labels')).not_to have_content "bug"
     end
+
+    it 'should remove label "enhancement"' do
+      find('.js-label-filter-remove', match: :first).click
+      wait_for_ajax
+      expect(find('.filtered-labels')).to have_no_content "enhancement"
+    end
   end
 
   context 'filter by label enhancement and bug in issues list', js: true do
@@ -164,4 +176,42 @@ feature 'Issue filtering by Labels', feature: true do
       expect(find('.filtered-labels')).not_to have_content "feature"
     end
   end
+
+  context 'remove filtered labels', js: true do
+    before do
+      page.within '.labels-filter' do
+        click_button 'Label'
+        wait_for_ajax
+        click_link 'bug'
+        find('.dropdown-menu-close').click
+      end
+
+      page.within '.filtered-labels' do
+        expect(page).to have_content 'bug'
+      end
+    end
+
+    it 'should allow user to remove filtered labels' do
+      first('.js-label-filter-remove').click
+      wait_for_ajax
+
+      expect(find('.filtered-labels', visible: false)).not_to have_content 'bug'
+      expect(find('.labels-filter')).not_to have_content 'bug'
+    end
+  end
+
+  context 'dropdown filtering', js: true do
+    it 'should filter by label name' do
+      page.within '.labels-filter' do
+        click_button 'Label'
+        wait_for_ajax
+        fill_in 'label-name', with: 'bug'
+
+        page.within '.dropdown-content' do
+          expect(page).not_to have_content 'enhancement'
+          expect(page).to have_content 'bug'
+        end
+      end
+    end
+  end
 end
diff --git a/spec/features/issues/filter_issues_spec.rb b/spec/features/issues/filter_issues_spec.rb
index 192e3619375c8eb801f0b63e5d4d6e28f766b23b..4bcb105b17d76f044d24400ee9dfc786b5dcd57b 100644
--- a/spec/features/issues/filter_issues_spec.rb
+++ b/spec/features/issues/filter_issues_spec.rb
@@ -1,6 +1,7 @@
 require 'rails_helper'
 
 describe 'Filter issues', feature: true do
+  include WaitForAjax
 
   let!(:project)   { create(:project) }
   let!(:user)      { create(:user)}
@@ -21,7 +22,7 @@ describe 'Filter issues', feature: true do
 
       find('.dropdown-menu-user-link', text: user.username).click
 
-      sleep 2
+      wait_for_ajax
     end
 
     context 'assignee', js: true do
@@ -53,7 +54,7 @@ describe 'Filter issues', feature: true do
 
       find('.milestone-filter .dropdown-content a', text: milestone.title).click
 
-      sleep 2
+      wait_for_ajax
     end
 
     context 'milestone', js: true do
@@ -80,23 +81,21 @@ describe 'Filter issues', feature: true do
     before do
       visit namespace_project_issues_path(project.namespace, project)
       find('.js-label-select').click
+      wait_for_ajax
     end
 
     it 'should filter by any label' do
       find('.dropdown-menu-labels a', text: 'Any Label').click
       page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
-      sleep 2
+      wait_for_ajax
 
-      page.within '.labels-filter' do
-        expect(page).to have_content 'Any Label'
-      end
-      expect(find('.js-label-select .dropdown-toggle-text')).to have_content('Any Label')
+      expect(find('.labels-filter')).to have_content 'Label'
     end
 
     it 'should filter by no label' do
       find('.dropdown-menu-labels a', text: 'No Label').click
       page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
-      sleep 2
+      wait_for_ajax
 
       page.within '.labels-filter' do
         expect(page).to have_content 'No Label'
@@ -122,14 +121,14 @@ describe 'Filter issues', feature: true do
 
       find('.dropdown-menu-user-link', text: user.username).click
 
-      sleep 2
+      wait_for_ajax
 
       find('.js-label-select').click
 
       find('.dropdown-menu-labels .dropdown-content a', text: label.title).click
       page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
 
-      sleep 2
+      wait_for_ajax
     end
 
     context 'assignee and label', js: true do
@@ -154,4 +153,148 @@ describe 'Filter issues', feature: true do
       end
     end
   end
+
+  describe 'filter issues by text' do
+    before do
+      create(:issue, title: "Bug", project: project)
+
+      bug_label = create(:label, project: project, title: 'bug')
+      milestone = create(:milestone, title: "8", project: project)
+
+      issue = create(:issue,
+        title: "Bug 2",
+        project: project,
+        milestone: milestone,
+        author: user,
+        assignee: user)
+      issue.labels << bug_label
+
+      visit namespace_project_issues_path(project.namespace, project)
+    end
+
+    context 'only text', js: true do
+      it 'should filter issues by searched text' do
+        fill_in 'issue_search', with: 'Bug'
+
+        page.within '.issues-list' do
+          expect(page).to have_selector('.issue', count: 2)
+        end
+      end
+
+      it 'should not show any issues' do
+        fill_in 'issue_search', with: 'testing'
+
+        page.within '.issues-list' do
+          expect(page).not_to have_selector('.issue')
+        end
+      end
+    end
+
+    context 'text and dropdown options', js: true do
+      it 'should filter by text and label' do
+        fill_in 'issue_search', with: 'Bug'
+
+        page.within '.issues-list' do
+          expect(page).to have_selector('.issue', count: 2)
+        end
+
+        click_button 'Label'
+        page.within '.labels-filter' do
+          click_link 'bug'
+        end
+
+        page.within '.issues-list' do
+          expect(page).to have_selector('.issue', count: 1)
+        end
+      end
+
+      it 'should filter by text and milestone' do
+        fill_in 'issue_search', with: 'Bug'
+
+        page.within '.issues-list' do
+          expect(page).to have_selector('.issue', count: 2)
+        end
+
+        click_button 'Milestone'
+        page.within '.milestone-filter' do
+          click_link '8'
+        end
+
+        page.within '.issues-list' do
+          expect(page).to have_selector('.issue', count: 1)
+        end
+      end
+
+      it 'should filter by text and assignee' do
+        fill_in 'issue_search', with: 'Bug'
+
+        page.within '.issues-list' do
+          expect(page).to have_selector('.issue', count: 2)
+        end
+
+        click_button 'Assignee'
+        page.within '.dropdown-menu-assignee' do
+          click_link user.name
+        end
+
+        page.within '.issues-list' do
+          expect(page).to have_selector('.issue', count: 1)
+        end
+      end
+
+      it 'should filter by text and author' do
+        fill_in 'issue_search', with: 'Bug'
+
+        page.within '.issues-list' do
+          expect(page).to have_selector('.issue', count: 2)
+        end
+
+        click_button 'Author'
+        page.within '.dropdown-menu-author' do
+          click_link user.name
+        end
+
+        page.within '.issues-list' do
+          expect(page).to have_selector('.issue', count: 1)
+        end
+      end
+    end
+  end
+
+  describe 'filter issues and sort', js: true do
+    before do
+      bug_label = create(:label, project: project, title: 'bug')
+      bug_one = create(:issue, title: "Frontend", project: project)
+      bug_two = create(:issue, title: "Bug 2", project: project)
+
+      bug_one.labels << bug_label
+      bug_two.labels << bug_label
+
+      visit namespace_project_issues_path(project.namespace, project)
+    end
+
+    it 'should be able to filter and sort issues' do
+      click_button 'Label'
+      wait_for_ajax
+      page.within '.labels-filter' do
+        click_link 'bug'
+      end
+      find('.dropdown-menu-close-icon').click
+      wait_for_ajax
+
+      page.within '.issues-list' do
+        expect(page).to have_selector('.issue', count: 2)
+      end
+
+      click_button 'Last created'
+      page.within '.dropdown-menu-sort' do
+        click_link 'Oldest created'
+      end
+      wait_for_ajax
+
+      page.within '.issues-list' do
+        expect(first('.issue')).to have_content('Frontend')
+      end
+    end
+  end
 end
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index 5739bc64dfb2b8c723000a459e4fbc03affb8197..45eda76a1fd375e3d027afdf89e9f9f314131286 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -73,6 +73,35 @@ feature 'Issue Sidebar', feature: true do
     end
   end
 
+  context 'updating weight', js: true do
+    before do
+      project.team << [user, :master]
+      visit_issue(project, issue)
+    end
+
+    it 'should update weight in sidebar to 1' do
+      page.within '.weight' do
+        click_link 'Edit'
+        click_link '1'
+
+        page.within '.value' do
+          expect(page).to have_content '1'
+        end
+      end
+    end
+
+    it 'should update weight in sidebar to no weight' do
+      page.within '.weight' do
+        click_link 'Edit'
+        click_link 'No Weight'
+
+        page.within '.value' do
+          expect(page).to have_content 'None'
+        end
+      end
+    end
+  end
+
   def visit_issue(project, issue)
     visit namespace_project_issue_path(project.namespace, project, issue)
   end
diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb
index 84c8e20ebaa6d03f870ea657d0ac88f4181a1bde..7773c486b4e8aeff5cf280c9180205f92f95b259 100644
--- a/spec/features/issues/move_spec.rb
+++ b/spec/features/issues/move_spec.rb
@@ -19,13 +19,14 @@ feature 'issue move to another project' do
     end
 
     scenario 'moving issue to another project not allowed' do
-      expect(page).to have_no_select('move_to_project_id')
+      expect(page).to have_no_selector('#move_to_project_id')
     end
   end
 
   context 'user has permission to move issue' do
     let!(:mr) { create(:merge_request, source_project: old_project) }
     let(:new_project) { create(:project) }
+    let(:new_project_search) { create(:project) }
     let(:text) { 'Text with !1' }
     let(:cross_reference) { old_project.to_reference }
 
@@ -37,7 +38,7 @@ feature 'issue move to another project' do
     end
 
     scenario 'moving issue to another project' do
-      select(new_project.name_with_namespace, from: 'move_to_project_id')
+      first('#move_to_project_id', visible: false).set(new_project.id)
       click_button('Save changes')
 
       expect(current_url).to include project_path(new_project)
@@ -47,14 +48,33 @@ feature 'issue move to another project' do
       expect(page).to have_content(issue.title)
     end
 
-    context 'projects user does not have permission to move issue to exist' do
+    scenario 'searching project dropdown', js: true do
+      new_project_search.team << [user, :reporter]
+
+      page.within '.js-move-dropdown' do
+        first('.select2-choice').click
+      end
+
+      fill_in('s2id_autogen2_search', with: new_project_search.name)
+
+      page.within '.select2-drop' do
+        expect(page).to have_content(new_project_search.name)
+        expect(page).not_to have_content(new_project.name)
+      end
+    end
+
+    context 'user does not have permission to move the issue to a project', js: true do
       let!(:private_project) { create(:project, :private) }
       let(:another_project) { create(:project) }
       background { another_project.team << [user, :guest] }
 
       scenario 'browsing projects in projects select' do
-        options = [ '', 'No project', new_project.name_with_namespace ]
-        expect(page).to have_select('move_to_project_id', options: options)
+        click_link 'Select project'
+
+        page.within '.select2-results' do
+          expect(page).to have_content 'No project'
+          expect(page).to have_content new_project.name_with_namespace
+        end
       end
     end
 
@@ -65,7 +85,7 @@ feature 'issue move to another project' do
       end
 
       scenario 'user wants to move issue that has already been moved' do
-        expect(page).to have_no_select('move_to_project_id')
+        expect(page).to have_no_selector('#move_to_project_id')
       end
     end
   end
diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb
index e4efdbe2421d0dfc5838dcba1bac30cb705c4e2b..f5cfe2d666ef93e3b1bc3f8a3887cfae7d621256 100644
--- a/spec/features/issues/note_polling_spec.rb
+++ b/spec/features/issues/note_polling_spec.rb
@@ -9,8 +9,11 @@ feature 'Issue notes polling' do
   end
 
   scenario 'Another user adds a comment to an issue', js: true do
-    note = create(:note_on_issue, noteable: issue, note: 'Looks good!')
+    note = create(:note, noteable: issue, project: project,
+                         note: 'Looks good!')
+
     page.execute_script('notes.refresh();')
+
     expect(page).to have_selector("#note_#{note.id}", text: 'Looks good!')
   end
 end
diff --git a/spec/features/issues/todo_spec.rb b/spec/features/issues/todo_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b69cce3e7d73f901ce9cedc69519abf3503dff77
--- /dev/null
+++ b/spec/features/issues/todo_spec.rb
@@ -0,0 +1,33 @@
+require 'rails_helper'
+
+feature 'Manually create a todo item from issue', feature: true, js: true do
+  let!(:project)   { create(:project) }
+  let!(:issue)     { create(:issue, project: project) }
+  let!(:user)      { create(:user)}
+
+  before do
+    project.team << [user, :master]
+    login_as(user)
+    visit namespace_project_issue_path(project.namespace, project, issue)
+  end
+
+  it 'should create todo when clicking button' do
+    page.within '.issuable-sidebar' do
+      click_button 'Add Todo'
+      expect(page).to have_content 'Mark Done'
+    end
+
+    page.within '.header-content .todos-pending-count' do
+      expect(page).to have_content '1'
+    end
+  end
+
+  it 'should mark a todo as done' do
+    page.within '.issuable-sidebar' do
+      click_button 'Add Todo'
+      click_button 'Mark Done'
+    end
+
+    expect(page).to have_selector('.todos-pending-count', visible: false)
+  end
+end
diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb
index b03dd0f666df56392a78b3492a602ab193bc8b30..ddbd69b28912af7ffa682bb2c24cc7622a6a472a 100644
--- a/spec/features/issues/update_issues_spec.rb
+++ b/spec/features/issues/update_issues_spec.rb
@@ -1,6 +1,8 @@
 require 'rails_helper'
 
 feature 'Multiple issue updating from issues#index', feature: true do
+  include WaitForAjax
+
   let!(:project)   { create(:project) }
   let!(:issue)     { create(:issue, project: project) }
   let!(:user)      { create(:user)}
@@ -24,9 +26,7 @@ feature 'Multiple issue updating from issues#index', feature: true do
 
     it 'should be set to open' do
       create_closed
-      visit namespace_project_issues_path(project.namespace, project)
-
-      find('.issues-state-filters a', text: 'Closed').click
+      visit namespace_project_issues_path(project.namespace, project, state: 'closed')
 
       find('#check_all_issues').click
       find('.js-issue-status').click
@@ -42,7 +42,7 @@ feature 'Multiple issue updating from issues#index', feature: true do
       visit namespace_project_issues_path(project.namespace, project)
 
       find('#check_all_issues').click
-      find('.js-update-assignee').click
+      click_update_assignee_button
 
       find('.dropdown-menu-user-link', text: user.username).click
       click_update_issues_button
@@ -57,14 +57,11 @@ feature 'Multiple issue updating from issues#index', feature: true do
       visit namespace_project_issues_path(project.namespace, project)
 
       find('#check_all_issues').click
-      find('.js-update-assignee').click
+      click_update_assignee_button
 
       click_link 'Unassigned'
       click_update_issues_button
-
-      within first('.issue .controls') do
-        expect(page).to have_no_selector('.author_link')
-      end
+      expect(find('.issue:first-child .controls')).not_to have_css('.author_link')
     end
   end
 
@@ -95,7 +92,7 @@ feature 'Multiple issue updating from issues#index', feature: true do
       find('.dropdown-menu-milestone a', text: "No Milestone").click
       click_update_issues_button
 
-      expect(first('.issue')).to_not have_content milestone.title
+      expect(find('.issue:first-child')).not_to have_content milestone.title
     end
   end
 
@@ -111,7 +108,13 @@ feature 'Multiple issue updating from issues#index', feature: true do
     create(:issue, project: project, milestone: milestone)
   end
 
+  def click_update_assignee_button
+    find('.js-update-assignee').click
+    wait_for_ajax
+  end
+
   def click_update_issues_button
     find('.update_selected_issues').click
+    wait_for_ajax
   end
 end
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index 49490848560faab5deac96ca7d1c27e902e90587..33274302c781c51ef774002da9c9f37b149a8b00 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -64,10 +64,70 @@ describe 'Issues', feature: true do
     end
   end
 
+  describe 'due date', js: true do
+    context 'on new form' do
+      before do
+        visit new_namespace_project_issue_path(project.namespace, project)
+      end
+
+      it 'should save with due date' do
+        date = Date.today.at_beginning_of_month
+
+        fill_in 'issue_title', with: 'bug 345'
+        fill_in 'issue_description', with: 'bug description'
+        find('#issuable-due-date').click
+
+        page.within '.ui-datepicker' do
+          click_link date.day
+        end
+
+        expect(find('#issuable-due-date').value).to eq date.to_s
+
+        click_button 'Submit issue'
+
+        page.within '.issuable-sidebar' do
+          expect(page).to have_content date.to_s(:medium)
+        end
+      end
+    end
+
+    context 'on edit form' do
+      let(:issue) { create(:issue, author: @user,project: project, due_date: Date.today.at_beginning_of_month.to_s) }
+
+      before do
+        visit edit_namespace_project_issue_path(project.namespace, project, issue)
+      end
+
+      it 'should save with due date' do
+        date = Date.today.at_beginning_of_month
+
+        expect(find('#issuable-due-date').value).to eq date.to_s
+
+        date = date.tomorrow
+
+        fill_in 'issue_title', with: 'bug 345'
+        fill_in 'issue_description', with: 'bug description'
+        find('#issuable-due-date').click
+
+        page.within '.ui-datepicker' do
+          click_link date.day
+        end
+
+        expect(find('#issuable-due-date').value).to eq date.to_s
+
+        click_button 'Save changes'
+
+        page.within '.issuable-sidebar' do
+          expect(page).to have_content date.to_s(:medium)
+        end
+      end
+    end
+  end
+
   describe 'Issue info' do
     it 'excludes award_emoji from comment count' do
       issue = create(:issue, author: @user, assignee: @user, project: project, title: 'foobar')
-      create(:upvote_note, noteable: issue)
+      create(:award_emoji, awardable: issue)
 
       visit namespace_project_issues_path(project.namespace, project, assignee_id: @user.id)
 
@@ -307,13 +367,9 @@ describe 'Issues', feature: true do
 
         page.within('.assignee') do
           expect(page).to have_content "#{@user.name}"
-        end
 
-        find('.block.assignee .edit-link').click
-        sleep 2 # wait for ajax stuff to complete
-        first('.dropdown-menu-user-link').click
-        sleep 2
-        page.within('.assignee') do
+          click_link 'Edit'
+          click_link 'Unassigned'
           expect(page).to have_content 'No assignee'
         end
 
@@ -331,7 +387,7 @@ describe 'Issues', feature: true do
         page.within '.assignee' do
           click_link 'Edit'
         end
-        
+
         page.within '.dropdown-menu-user' do
           click_link @user.name
         end
@@ -340,6 +396,27 @@ describe 'Issues', feature: true do
           expect(page).to have_content @user.name
         end
       end
+
+      it 'allows user to unselect themselves', js: true do
+        issue2 = create(:issue, project: project, author: @user)
+        visit namespace_project_issue_path(project.namespace, project, issue2)
+
+        page.within '.assignee' do
+          click_link 'Edit'
+          click_link @user.name
+
+          page.within '.value' do
+            expect(page).to have_content @user.name
+          end
+
+          click_link 'Edit'
+          click_link @user.name
+
+          page.within '.value' do
+            expect(page).to have_content "No assignee"
+          end
+        end
+      end
     end
 
     context 'by unauthorized user' do
@@ -405,6 +482,26 @@ describe 'Issues', feature: true do
 
         expect(issue.reload.milestone).to be_nil
       end
+
+      it 'allows user to de-select milestone', js: true do
+        visit namespace_project_issue_path(project.namespace, project, issue)
+
+        page.within('.milestone') do
+          click_link 'Edit'
+          click_link milestone.title
+
+          page.within '.value' do
+            expect(page).to have_content milestone.title
+          end
+
+          click_link 'Edit'
+          click_link milestone.title
+
+          page.within '.value' do
+            expect(page).to have_content 'None'
+          end
+        end
+      end
     end
 
     context 'by unauthorized user' do
@@ -452,6 +549,43 @@ describe 'Issues', feature: true do
     end
   end
 
+  describe 'due date' do
+    context 'update due on issue#show', js: true do
+      let(:issue) { create(:issue, project: project, author: @user, assignee: @user) }
+
+      before do
+        visit namespace_project_issue_path(project.namespace, project, issue)
+      end
+
+      it 'should add due date to issue' do
+        page.within '.due_date' do
+          click_link 'Edit'
+
+          page.within '.ui-datepicker-calendar' do
+            first('.ui-state-default').click
+          end
+
+          expect(page).to have_no_content 'None'
+        end
+      end
+
+      it 'should remove due date from issue' do
+        page.within '.due_date' do
+          click_link 'Edit'
+
+          page.within '.ui-datepicker-calendar' do
+            first('.ui-state-default').click
+          end
+
+          expect(page).to have_no_content 'No due date'
+
+          click_link 'remove due date'
+          expect(page).to have_content 'No due date'
+        end
+      end
+    end
+  end
+
   def first_issue
     page.all('ul.issues-list > li').first.text
   end
diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb
index 8c38dd5b122eec74a169314be9577e278e3e0d15..72b5ff231f7cde9a6a0877d8d313c6d227d5e1ec 100644
--- a/spec/features/login_spec.rb
+++ b/spec/features/login_spec.rb
@@ -32,12 +32,12 @@ feature 'Login', feature: true do
       let(:user) { create(:user, :two_factor) }
 
       before do
-        login_with(user)
-        expect(page).to have_content('Two-factor Authentication')
+        login_with(user, remember: true)
+        expect(page).to have_content('Two-Factor Authentication')
       end
 
       def enter_code(code)
-        fill_in 'Two-factor Authentication code', with: code
+        fill_in 'Two-Factor Authentication code', with: code
         click_button 'Verify code'
       end
 
@@ -52,6 +52,12 @@ feature 'Login', feature: true do
           expect(current_path).to eq root_path
         end
 
+        it 'persists remember_me value via hidden field' do
+          field = first('input#user_remember_me', visible: false)
+
+          expect(field.value).to eq '1'
+        end
+
         it 'blocks login with invalid code' do
           enter_code('foo')
           expect(page).to have_content('Invalid two-factor code')
@@ -121,7 +127,7 @@ feature 'Login', feature: true do
       user = create(:user, password: 'not-the-default')
 
       login_with(user)
-      expect(page).to have_content('Invalid login or password.')
+      expect(page).to have_content('Invalid Login or password.')
     end
   end
 
@@ -137,12 +143,12 @@ feature 'Login', feature: true do
 
       context 'within the grace period' do
         it 'redirects to two-factor configuration page' do
-          expect(current_path).to eq new_profile_two_factor_auth_path
-          expect(page).to have_content('You must enable Two-factor Authentication for your account before')
+          expect(current_path).to eq profile_two_factor_auth_path
+          expect(page).to have_content('You must enable Two-Factor Authentication for your account before')
         end
 
-        it 'disallows skipping two-factor configuration' do
-          expect(current_path).to eq new_profile_two_factor_auth_path
+        it 'allows skipping two-factor configuration', js: true do
+          expect(current_path).to eq profile_two_factor_auth_path
 
           click_link 'Configure it later'
           expect(current_path).to eq root_path
@@ -153,26 +159,26 @@ feature 'Login', feature: true do
         let(:user) { create(:user, otp_grace_period_started_at: 9999.hours.ago) }
 
         it 'redirects to two-factor configuration page' do
-          expect(current_path).to eq new_profile_two_factor_auth_path
-          expect(page).to have_content('You must enable Two-factor Authentication for your account.')
+          expect(current_path).to eq profile_two_factor_auth_path
+          expect(page).to have_content('You must enable Two-Factor Authentication for your account.')
         end
 
-        it 'disallows skipping two-factor configuration' do
-          expect(current_path).to eq new_profile_two_factor_auth_path
+        it 'disallows skipping two-factor configuration', js: true do
+          expect(current_path).to eq profile_two_factor_auth_path
           expect(page).not_to have_link('Configure it later')
         end
       end
     end
 
-    context 'without grace pariod defined' do
+    context 'without grace period defined' do
       before(:each) do
         stub_application_setting(two_factor_grace_period: 0)
         login_with(user)
       end
 
       it 'redirects to two-factor configuration page' do
-        expect(current_path).to eq new_profile_two_factor_auth_path
-        expect(page).to have_content('You must enable Two-factor Authentication for your account.')
+        expect(current_path).to eq profile_two_factor_auth_path
+        expect(page).to have_content('You must enable Two-Factor Authentication for your account.')
       end
     end
   end
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
index 1d892fe1a55b85e4686cd6c3956bbafc78f8c06e..09ccc77c101780bcac82fde639ac78509ba0ffad 100644
--- a/spec/features/markdown_spec.rb
+++ b/spec/features/markdown_spec.rb
@@ -165,22 +165,32 @@ describe 'GitLab Markdown', feature: true do
     describe 'ExternalLinkFilter' do
       it 'adds nofollow to external link' do
         link = doc.at_css('a:contains("Google")')
+
         expect(link.attr('rel')).to include('nofollow')
       end
 
       it 'adds noreferrer to external link' do
         link = doc.at_css('a:contains("Google")')
+
         expect(link.attr('rel')).to include('noreferrer')
       end
 
+      it 'adds _blank to target attribute for external links' do
+        link = doc.at_css('a:contains("Google")')
+
+        expect(link.attr('target')).to match('_blank')
+      end
+
       it 'ignores internal link' do
         link = doc.at_css('a:contains("GitLab Root")')
+
         expect(link.attr('rel')).not_to match 'nofollow'
+        expect(link.attr('target')).not_to match '_blank'
       end
     end
   end
 
-  before(:all) do
+  before do
     @feat = MarkdownFeature.new
 
     # `markdown` helper expects a `@project` variable
@@ -188,7 +198,7 @@ describe 'GitLab Markdown', feature: true do
   end
 
   context 'default pipeline' do
-    before(:all) do
+    before do
       @html = markdown(@feat.raw_markdown)
     end
 
@@ -231,13 +241,14 @@ describe 'GitLab Markdown', feature: true do
   context 'wiki pipeline' do
     before do
       @project_wiki = @feat.project_wiki
+      @project_wiki_page = @feat.project_wiki_page
 
       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)
       allow(@project_wiki).to receive(:wiki_base_path) { '/namespace1/gitlabhq/wikis' }
 
-      @html = markdown(@feat.raw_markdown, { pipeline: :wiki, project_wiki: @project_wiki })
+      @html = markdown(@feat.raw_markdown, { pipeline: :wiki, project_wiki: @project_wiki, page_slug: @project_wiki_page.slug })
     end
 
     it_behaves_like 'all pipelines'
diff --git a/spec/features/merge_requests/award_spec.rb b/spec/features/merge_requests/award_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..007f67d60804e11bffc33c4675ad7614e89a76a5
--- /dev/null
+++ b/spec/features/merge_requests/award_spec.rb
@@ -0,0 +1,49 @@
+require 'rails_helper'
+
+feature 'Merge request awards', js: true, feature: true do
+  let(:user) { create(:user) }
+  let(:project) { create(:project, :public) }
+  let(:merge_request) { create(:merge_request, source_project: project) }
+
+  describe 'logged in' do
+    before do
+      login_as(user)
+      visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+    end
+
+    it 'should add award to merge request' do
+      first('.js-emoji-btn').click
+      expect(page).to have_selector('.js-emoji-btn.active')
+      expect(first('.js-emoji-btn')).to have_content '1'
+
+      visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+      expect(first('.js-emoji-btn')).to have_content '1'
+    end
+
+    it 'should remove award from merge request' do
+      first('.js-emoji-btn').click
+      find('.js-emoji-btn.active').click
+      expect(first('.js-emoji-btn')).to have_content '0'
+
+      visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+      expect(first('.js-emoji-btn')).to have_content '0'
+    end
+
+    it 'should only have one menu on the page' do
+      first('.js-add-award').click
+      expect(page).to have_selector('.emoji-menu')
+
+      expect(page).to have_selector('.emoji-menu', count: 1)
+    end
+  end
+
+  describe 'logged out' do
+    before do
+      visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+    end
+
+    it 'should not see award menu button' do
+      expect(page).not_to have_selector('.js-award-holder')
+    end
+  end
+end
diff --git a/spec/features/merge_requests/created_from_fork_spec.rb b/spec/features/merge_requests/created_from_fork_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b4d2201c7295de7759500cca9e7dac382a3e9bc1
--- /dev/null
+++ b/spec/features/merge_requests/created_from_fork_spec.rb
@@ -0,0 +1,58 @@
+require 'spec_helper'
+
+feature 'Merge request created from fork' do
+  given(:user) { create(:user) }
+  given(:project) { create(:project, :public) }
+  given(:fork_project) { create(:project, :public) }
+
+  given!(:merge_request) do
+    create(:forked_project_link, forked_to_project: fork_project,
+                                 forked_from_project: project)
+
+    create(:merge_request_with_diffs, source_project: fork_project,
+                                      target_project: project,
+                                      description: 'Test merge request')
+  end
+
+  background do
+    fork_project.team << [user, :master]
+    login_as user
+  end
+
+  scenario 'user can access merge request' do
+    visit_merge_request(merge_request)
+
+    expect(page).to have_content 'Test merge request'
+  end
+
+  context 'pipeline present in source project' do
+    include WaitForAjax
+
+    given(:pipeline) do
+      create(:ci_pipeline_with_two_job, project: fork_project,
+                                        sha: merge_request.last_commit.id,
+                                        ref: merge_request.source_branch)
+    end
+
+    background { pipeline.create_builds(user) }
+
+    scenario 'user visits a pipelines page', js: true do
+      visit_merge_request(merge_request)
+      page.within('.merge-request-tabs') { click_link 'Builds' }
+      wait_for_ajax
+
+      page.within('table.builds') do
+        expect(page).to have_content 'rspec'
+        expect(page).to have_content 'spinach'
+      end
+
+      expect(find_link('Cancel running')[:href])
+        .to include fork_project.path_with_namespace
+    end
+  end
+
+  def visit_merge_request(mr)
+    visit namespace_project_merge_request_path(project.namespace,
+                                               project, mr)
+  end
+end
diff --git a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb
index 7aa7eb965e908ee173aeda4fff7b815414389a2d..c5e6412d7bfdb0593c02c83b65740a1d4a949a7c 100644
--- a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb
+++ b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb
@@ -12,8 +12,8 @@ feature 'Merge When Build Succeeds', feature: true, js: true do
   end
 
   context "Active build for Merge Request" do
-    let!(:ci_commit) { create(:ci_commit, project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) }
-    let!(:ci_build) { create(:ci_build, commit: ci_commit) }
+    let!(:pipeline) { create(:ci_pipeline, project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) }
+    let!(:ci_build) { create(:ci_build, pipeline: pipeline) }
 
     before do
       login_as user
@@ -47,8 +47,8 @@ feature 'Merge When Build Succeeds', feature: true, js: true do
                                                   merge_user: user, title: "MepMep", merge_when_build_succeeds: true)
     end
 
-    let!(:ci_commit) { create(:ci_commit, project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) }
-    let!(:ci_build) { create(:ci_build, commit: ci_commit) }
+    let!(:pipeline) { create(:ci_pipeline, project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) }
+    let!(:ci_build) { create(:ci_build, pipeline: pipeline) }
 
     before do
       login_as user
diff --git a/spec/features/merge_requests/only_allow_merge_if_build_succeeds.rb b/spec/features/merge_requests/only_allow_merge_if_build_succeeds.rb
new file mode 100644
index 0000000000000000000000000000000000000000..65e9185ec2441c9355102edcdd55ccb402ddc04b
--- /dev/null
+++ b/spec/features/merge_requests/only_allow_merge_if_build_succeeds.rb
@@ -0,0 +1,105 @@
+require 'spec_helper'
+
+feature 'Only allow merge requests to be merged if the build succeeds', feature: true do
+  let(:project)       { create(:project, :public) }
+  let(:merge_request) { create(:merge_request_with_diffs, source_project: project) }
+
+  before do
+    login_as merge_request.author
+
+    project.team << [merge_request.author, :master]
+  end
+
+  context 'project does not have CI enabled' do
+    it 'allows MR to be merged' do
+      visit_merge_request(merge_request)
+
+      expect(page).to have_button 'Accept Merge Request'
+    end
+  end
+
+  context 'when project has CI enabled' do
+    let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) }
+
+    context 'when merge requests can only be merged if the build succeeds' do
+      before do
+        project.update_attribute(:only_allow_merge_if_build_succeeds, true)
+      end
+
+      context 'when CI is running' do
+        before { pipeline.update_column(:status, :running) }
+
+        it 'does not allow to merge immediately' do
+          visit_merge_request(merge_request)
+
+          expect(page).to have_button 'Merge When Build Succeeds'
+          expect(page).not_to have_button 'Select Merge Moment'
+        end
+      end
+
+      context 'when CI failed' do
+        before { pipeline.update_column(:status, :failed) }
+
+        it 'does not allow MR to be merged' do
+          visit_merge_request(merge_request)
+
+          expect(page).not_to have_button 'Accept Merge Request'
+          expect(page).to have_content('Please retry the build or push a new commit to fix the failure.')
+        end
+      end
+
+      context 'when CI succeeded' do
+        before { pipeline.update_column(:status, :success) }
+
+        it 'allows MR to be merged' do
+          visit_merge_request(merge_request)
+
+          expect(page).to have_button 'Accept Merge Request'
+        end
+      end
+    end
+
+    context 'when merge requests can be merged when the build failed' do
+      before do
+        project.update_attribute(:only_allow_merge_if_build_succeeds, false)
+      end
+
+      context 'when CI is running' do
+        before { pipeline.update_column(:status, :running) }
+
+        it 'allows MR to be merged immediately', js: true do
+          visit_merge_request(merge_request)
+
+          expect(page).to have_button 'Merge When Build Succeeds'
+
+          click_button 'Select Merge Moment'
+          expect(page).to have_content 'Merge Immediately'
+        end
+      end
+
+      context 'when CI failed' do
+        before { pipeline.update_column(:status, :failed) }
+
+        it 'allows MR to be merged' do
+          visit_merge_request(merge_request)
+
+          expect(page).to have_button 'Accept Merge Request'
+        end
+      end
+
+      context 'when CI succeeded' do
+        before { pipeline.update_column(:status, :success) }
+
+        it 'allows MR to be merged' do
+          visit_merge_request(merge_request)
+
+          expect(page).to have_button 'Accept Merge Request'
+        end
+      end
+    end
+  end
+
+  def visit_merge_request(merge_request)
+    visit namespace_project_merge_request_path(merge_request.project.namespace, merge_request.project, merge_request)
+  end
+end
diff --git a/spec/features/merge_requests/user_lists_merge_requests_spec.rb b/spec/features/merge_requests/user_lists_merge_requests_spec.rb
index 2c7e1c748adfdda5b2567cb8b5412a02a6638ffc..1c130057c5693a3063849b15a946378105d59ef9 100644
--- a/spec/features/merge_requests/user_lists_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_lists_merge_requests_spec.rb
@@ -131,6 +131,15 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true
         expect(first_merge_request).to include('fix')
         expect(count_merge_requests).to eq(1)
       end
+
+      it 'sorts by recently due milestone' do
+        visit namespace_project_merge_requests_path(project.namespace, project,
+          label_name: [label.name, label2.name],
+          assignee_id: user.id,
+          sort: sort_value_milestone_soon)
+
+        expect(first_merge_request).to include('fix')
+      end
     end
   end
 
diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb
index 9e9fec01943368eff2ed82ca8473b50bf01110d5..737efcef45d0122fb8385f2e7d18c79c90f5bcf1 100644
--- a/spec/features/notes_on_merge_requests_spec.rb
+++ b/spec/features/notes_on_merge_requests_spec.rb
@@ -4,25 +4,15 @@ describe 'Comments', feature: true do
   include RepoHelpers
   include WaitForAjax
 
-  describe 'On merge requests page', feature: true do
-    it 'excludes award_emoji from comment count' do
-      merge_request = create(:merge_request)
-      project = merge_request.source_project
-      create(:upvote_note, noteable: merge_request, project: project)
-
-      login_as :admin
-      visit namespace_project_merge_requests_path(project.namespace, project)
-
-      expect(merge_request.mr_and_commit_notes.count).to eq 1
-      expect(page.all('.merge-request-no-comments').first.text).to eq "0"
+  describe 'On a merge request', js: true, feature: true do
+    let!(:project) { create(:project) }
+    let!(:merge_request) do
+      create(:merge_request, source_project: project, target_project: project)
     end
-  end
 
-  describe 'On a merge request', js: true, feature: true do
-    let!(:merge_request) { create(:merge_request) }
-    let!(:project) { merge_request.source_project }
     let!(:note) do
-      create(:note_on_merge_request, :with_attachment, project: project)
+      create(:note_on_merge_request, :with_attachment, noteable: merge_request,
+                                                       project: project)
     end
 
     before do
@@ -143,17 +133,6 @@ describe 'Comments', feature: true do
         end
       end
     end
-
-    describe 'comment info' do
-      it 'excludes award_emoji from comment count' do
-        create(:upvote_note, noteable: merge_request, project: project)
-
-        visit namespace_project_merge_request_path(project.namespace, project, merge_request)
-
-        expect(merge_request.mr_and_commit_notes.count).to eq 2
-        expect(find('.notes-tab span.badge').text).to eq "1"
-      end
-    end
   end
 
   describe 'On a merge request diff', js: true, feature: true do
diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb
index 1adab7e9c6c3ef66f70eed0ecf83d0f081b8359b..c7c00a3266a32cfe5bd3e84301e7861b75d1e64f 100644
--- a/spec/features/participants_autocomplete_spec.rb
+++ b/spec/features/participants_autocomplete_spec.rb
@@ -32,7 +32,8 @@ feature 'Member autocomplete', feature: true do
   context 'adding a new note on a Issue', js: true do
     before do
       issue = create(:issue, author: author, project: project)
-      create(:note, note: 'Ultralight Beam', noteable: issue, author: participant)
+      create(:note, note: 'Ultralight Beam', noteable: issue,
+                    project: project, author: participant)
       visit_issue(project, issue)
     end
 
@@ -47,7 +48,8 @@ feature 'Member autocomplete', feature: true do
   context 'adding a new note on a Merge Request ', js: true do
     before do
       merge = create(:merge_request, source_project: project, target_project: project, author: author)
-      create(:note, note: 'Ultralight Beam', noteable: merge, author: participant)
+      create(:note, note: 'Ultralight Beam', noteable: merge,
+                    project: project, author: participant)
       visit_merge_request(project, merge)
     end
 
diff --git a/spec/features/pipelines_spec.rb b/spec/features/pipelines_spec.rb
index 1d6f4485c816b001553adf84584175f595087a99..98703ef3ac4b9b53e011d901f1ec1cf845ba4ab8 100644
--- a/spec/features/pipelines_spec.rb
+++ b/spec/features/pipelines_spec.rb
@@ -12,7 +12,7 @@ describe "Pipelines" do
   end
 
   describe 'GET /:project/pipelines' do
-    let!(:pipeline) { create(:ci_commit, project: project, ref: 'master', status: 'running') }
+    let!(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', status: 'running') }
 
     [:all, :running, :branches].each do |scope|
       context "displaying #{scope}" do
@@ -31,7 +31,7 @@ describe "Pipelines" do
     end
 
     context 'cancelable pipeline' do
-      let!(:running) { create(:ci_build, :running, commit: pipeline, stage: 'test', commands: 'test') }
+      let!(:running) { create(:ci_build, :running, pipeline: pipeline, stage: 'test', commands: 'test') }
 
       before { visit namespace_project_pipelines_path(project.namespace, project) }
 
@@ -41,13 +41,13 @@ describe "Pipelines" do
       context 'when canceling' do
         before { click_link('Cancel') }
 
-        it { expect(page).to_not have_link('Cancel') }
+        it { expect(page).not_to have_link('Cancel') }
         it { expect(page).to have_selector('.ci-canceled') }
       end
     end
 
     context 'retryable pipelines' do
-      let!(:failed) { create(:ci_build, :failed, commit: pipeline, stage: 'test', commands: 'test') }
+      let!(:failed) { create(:ci_build, :failed, pipeline: pipeline, stage: 'test', commands: 'test') }
 
       before { visit namespace_project_pipelines_path(project.namespace, project) }
 
@@ -57,14 +57,44 @@ describe "Pipelines" do
       context 'when retrying' do
         before { click_link('Retry') }
 
-        it { expect(page).to_not have_link('Retry') }
+        it { expect(page).not_to have_link('Retry') }
         it { expect(page).to have_selector('.ci-pending') }
       end
     end
 
+    context 'for generic statuses' do
+      context 'when running' do
+        let!(:running) { create(:generic_commit_status, status: 'running', pipeline: pipeline, stage: 'test') }
+
+        before { visit namespace_project_pipelines_path(project.namespace, project) }
+
+        it 'not be cancelable' do
+          expect(page).not_to have_link('Cancel')
+        end
+
+        it 'pipeline is running' do
+          expect(page).to have_selector('.ci-running')
+        end
+      end
+
+      context 'when failed' do
+        let!(:running) { create(:generic_commit_status, status: 'failed', pipeline: pipeline, stage: 'test') }
+
+        before { visit namespace_project_pipelines_path(project.namespace, project) }
+
+        it 'not be retryable' do
+          expect(page).not_to have_link('Retry')
+        end
+
+        it 'pipeline is failed' do
+          expect(page).to have_selector('.ci-failed')
+        end
+      end
+    end
+
     context 'downloadable pipelines' do
       context 'with artifacts' do
-        let!(:with_artifacts) { create(:ci_build, :artifacts, :success, commit: pipeline, name: 'rspec tests', stage: 'test') }
+        let!(:with_artifacts) { create(:ci_build, :artifacts, :success, pipeline: pipeline, name: 'rspec tests', stage: 'test') }
 
         before { visit namespace_project_pipelines_path(project.namespace, project) }
 
@@ -73,21 +103,21 @@ describe "Pipelines" do
       end
 
       context 'without artifacts' do
-        let!(:without_artifacts) { create(:ci_build, :success, commit: pipeline, name: 'rspec', stage: 'test') }
+        let!(:without_artifacts) { create(:ci_build, :success, pipeline: pipeline, name: 'rspec', stage: 'test') }
 
-        it { expect(page).to_not have_selector('.build-artifacts') }
+        it { expect(page).not_to have_selector('.build-artifacts') }
       end
     end
   end
 
   describe 'GET /:project/pipelines/:id' do
-    let(:pipeline) { create(:ci_commit, project: project, ref: 'master') }
+    let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master') }
 
     before do
-      @success = create(:ci_build, :success, commit: pipeline, stage: 'build', name: 'build')
-      @failed = create(:ci_build, :failed, commit: pipeline, stage: 'test', name: 'test', commands: 'test')
-      @running = create(:ci_build, :running, commit: pipeline, stage: 'deploy', name: 'deploy')
-      @external = create(:generic_commit_status, status: 'success', commit: pipeline, name: 'jenkins', stage: 'external')
+      @success = create(:ci_build, :success, pipeline: pipeline, stage: 'build', name: 'build')
+      @failed = create(:ci_build, :failed, pipeline: pipeline, stage: 'test', name: 'test', commands: 'test')
+      @running = create(:ci_build, :running, pipeline: pipeline, stage: 'deploy', name: 'deploy')
+      @external = create(:generic_commit_status, status: 'success', pipeline: pipeline, name: 'jenkins', stage: 'external')
     end
 
     before { visit namespace_project_pipeline_path(project.namespace, project, pipeline) }
@@ -104,23 +134,23 @@ describe "Pipelines" do
     end
 
     context 'retrying builds' do
-      it { expect(page).to_not have_content('retried') }
+      it { expect(page).not_to have_content('retried') }
 
       context 'when retrying' do
         before { click_on 'Retry failed' }
 
-        it { expect(page).to_not have_content('Retry failed') }
+        it { expect(page).not_to have_content('Retry failed') }
         it { expect(page).to have_content('retried') }
       end
     end
 
     context 'canceling builds' do
-      it { expect(page).to_not have_selector('.ci-canceled') }
+      it { expect(page).not_to have_selector('.ci-canceled') }
 
       context 'when canceling' do
         before { click_on 'Cancel running' }
 
-        it { expect(page).to_not have_content('Cancel running') }
+        it { expect(page).not_to have_content('Cancel running') }
         it { expect(page).to have_selector('.ci-canceled') }
       end
     end
@@ -135,9 +165,9 @@ describe "Pipelines" do
       before { fill_in('Create for', with: 'master') }
 
       context 'with gitlab-ci.yml' do
-        before { stub_ci_commit_to_return_yaml_file }
+        before { stub_ci_pipeline_to_return_yaml_file }
 
-        it { expect{ click_on 'Create pipeline' }.to change{ Ci::Commit.count }.by(1) }
+        it { expect{ click_on 'Create pipeline' }.to change{ Ci::Pipeline.count }.by(1) }
       end
 
       context 'without gitlab-ci.yml' do
diff --git a/spec/features/profiles/preferences_spec.rb b/spec/features/profiles/preferences_spec.rb
index 8f645438cffce3a9d3d0eb53452390b59ece8aa0..787bf42d0487ffa8cfa191f93a11bdf959a7b63d 100644
--- a/spec/features/profiles/preferences_spec.rb
+++ b/spec/features/profiles/preferences_spec.rb
@@ -54,7 +54,7 @@ describe 'Profile > Preferences', feature: true do
     end
   end
 
-  describe 'User changes their default dashboard' do
+  describe 'User changes their default dashboard', js: true do
     it 'creates a flash message' do
       select 'Starred Projects', from: 'user_dashboard'
       click_button 'Save'
@@ -66,8 +66,10 @@ describe 'Profile > Preferences', feature: true do
       select 'Starred Projects', from: 'user_dashboard'
       click_button 'Save'
 
-      click_link 'Dashboard'
-      expect(page.current_path).to eq starred_dashboard_projects_path
+      allowing_for_delay do
+        find('#logo').click
+        expect(page.current_path).to eq starred_dashboard_projects_path
+      end
 
       click_link 'Your Projects'
       expect(page.current_path).to eq dashboard_projects_path
diff --git a/spec/features/projects/badges/list_spec.rb b/spec/features/projects/badges/list_spec.rb
index 13c9b95b3169a37a0c82e146eb699d0ee8364e48..51be81d634c17e30119c1ae7b1328366ecc0d09e 100644
--- a/spec/features/projects/badges/list_spec.rb
+++ b/spec/features/projects/badges/list_spec.rb
@@ -8,12 +8,10 @@ feature 'list of badges' do
     project = create(:project)
     project.team << [user, :master]
     login_as(user)
-    visit edit_namespace_project_path(project.namespace, project)
+    visit namespace_project_badges_path(project.namespace, project)
   end
 
   scenario 'user displays list of badges' do
-    click_link 'Badges'
-
     expect(page).to have_content 'build status'
     expect(page).to have_content 'Markdown'
     expect(page).to have_content 'HTML'
@@ -26,7 +24,6 @@ feature 'list of badges' do
   end
 
   scenario 'user changes current ref on badges list page', js: true do
-    click_link 'Badges'
     select2('improve/awesome', from: '#ref')
 
     expect(page).to have_content 'badges/improve/awesome/build.svg'
diff --git a/spec/features/projects/commit/builds_spec.rb b/spec/features/projects/commit/builds_spec.rb
index 40ba0bdc1157e93cbb3be663cc990647f19910b4..15c381c0f5a726e2fc8d6eef48ce3b09d024da7f 100644
--- a/spec/features/projects/commit/builds_spec.rb
+++ b/spec/features/projects/commit/builds_spec.rb
@@ -11,9 +11,9 @@ feature 'project commit builds' do
 
   context 'when no builds triggered yet' do
     background do
-      create(:ci_commit, project: project,
-                         sha: project.commit.sha,
-                         ref: 'master')
+      create(:ci_pipeline, project: project,
+                           sha: project.commit.sha,
+                           ref: 'master')
     end
 
     scenario 'user views commit builds page' do
diff --git a/spec/features/projects/commits/cherry_pick_spec.rb b/spec/features/projects/commits/cherry_pick_spec.rb
index 0559b02f3218f6130c061fb4b756b29636a0d844..f88c0616b52abd827b793bc9f2c98bb6c4dbb3c2 100644
--- a/spec/features/projects/commits/cherry_pick_spec.rb
+++ b/spec/features/projects/commits/cherry_pick_spec.rb
@@ -16,6 +16,7 @@ describe 'Cherry-pick Commits' do
     it do
       visit namespace_project_commit_path(project.namespace, project, master_pickable_commit.id)
       find("a[href='#modal-cherry-pick-commit']").click
+      expect(page).not_to have_content('v1.0.0') # Only branches, not tags
       page.within('#modal-cherry-pick-commit') do
         uncheck 'create_merge_request'
         click_button 'Cherry-pick'
diff --git a/spec/features/projects/files/gitignore_dropdown_spec.rb b/spec/features/projects/files/gitignore_dropdown_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..073a83b68964315ce1df32ade55a42cfe4db5717
--- /dev/null
+++ b/spec/features/projects/files/gitignore_dropdown_spec.rb
@@ -0,0 +1,30 @@
+require 'spec_helper'
+
+feature 'User wants to add a .gitignore file', feature: true do
+  include WaitForAjax
+
+  before do
+    user = create(:user)
+    project = create(:project)
+    project.team << [user, :master]
+    login_as user
+    visit namespace_project_new_blob_path(project.namespace, project, 'master', file_name: '.gitignore')
+  end
+
+  scenario 'user can see .gitignore dropdown' do
+    expect(page).to have_css('.gitignore-selector')
+  end
+
+  scenario 'user can pick a .gitignore file from the dropdown', js: true do
+    find('.js-gitignore-selector').click
+    wait_for_ajax
+    within '.gitignore-selector' do
+      find('.dropdown-input-field').set('rails')
+      find('.dropdown-content li', text: 'Rails').click
+    end
+    wait_for_ajax
+
+    expect(page).to have_content('/.bundle')
+    expect(page).to have_content('# Gemfile.lock, .ruby-version, .ruby-gemset')
+  end
+end
diff --git a/spec/features/projects/files/project_owner_creates_license_file_spec.rb b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
index 3d6ffbc4c6bc97be8eeea498b22933349284d9ab..ecc818eb1e151ce784d5a0cd4cdc3eb047ccda4b 100644
--- a/spec/features/projects/files/project_owner_creates_license_file_spec.rb
+++ b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
@@ -25,7 +25,7 @@ feature 'project owner creates a license file', feature: true, js: true do
 
     file_content = find('.file-content')
     expect(file_content).to have_content('The MIT License (MIT)')
-    expect(file_content).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
+    expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
 
     fill_in :commit_message, with: 'Add a LICENSE file', visible: true
     click_button 'Commit Changes'
@@ -33,7 +33,7 @@ feature 'project owner creates a license file', feature: true, js: true do
     expect(current_path).to eq(
       namespace_project_blob_path(project.namespace, project, 'master/LICENSE'))
     expect(page).to have_content('The MIT License (MIT)')
-    expect(page).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
+    expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
   end
 
   scenario 'project master creates a license file from the "Add license" link' do
@@ -48,7 +48,7 @@ feature 'project owner creates a license file', feature: true, js: true do
 
     file_content = find('.file-content')
     expect(file_content).to have_content('The MIT License (MIT)')
-    expect(file_content).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
+    expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
 
     fill_in :commit_message, with: 'Add a LICENSE file', visible: true
     click_button 'Commit Changes'
@@ -56,6 +56,6 @@ feature 'project owner creates a license file', feature: true, js: true do
     expect(current_path).to eq(
       namespace_project_blob_path(project.namespace, project, 'master/LICENSE'))
     expect(page).to have_content('The MIT License (MIT)')
-    expect(page).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
+    expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
   end
 end
diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
index 3268e240200e6a983b3c1c22fc47144388760ffa..34eda29c2852a0126e81eb8b2f22221eee028db9 100644
--- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
+++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
@@ -24,7 +24,7 @@ feature 'project owner sees a link to create a license file in empty project', f
 
     file_content = find('.file-content')
     expect(file_content).to have_content('The MIT License (MIT)')
-    expect(file_content).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
+    expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
 
     fill_in :commit_message, with: 'Add a LICENSE file', visible: true
     # Remove pre-receive hook so we can push without auth
@@ -34,6 +34,6 @@ feature 'project owner sees a link to create a license file in empty project', f
     expect(current_path).to eq(
       namespace_project_blob_path(project.namespace, project, 'master/LICENSE'))
     expect(page).to have_content('The MIT License (MIT)')
-    expect(page).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
+    expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
   end
 end
diff --git a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..461f1737928908d57c42b524673a601d48b98034
--- /dev/null
+++ b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
@@ -0,0 +1,87 @@
+require 'spec_helper'
+
+feature 'Issue prioritization', feature: true do
+
+  let(:user)    { create(:user) }
+  let(:project) { create(:project, name: 'test', namespace: user.namespace) }
+
+  # Labels
+  let(:label_1) { create(:label, title: 'label_1', project: project, priority: 1) }
+  let(:label_2) { create(:label, title: 'label_2', project: project, priority: 2) }
+  let(:label_3) { create(:label, title: 'label_3', project: project, priority: 3) }
+  let(:label_4) { create(:label, title: 'label_4', project: project, priority: 4) }
+  let(:label_5) { create(:label, title: 'label_5', project: project) } # no priority
+
+  # According to https://gitlab.com/gitlab-org/gitlab-ce/issues/14189#note_4360653
+  context 'when issues have one label' do
+    scenario 'Are sorted properly' do
+
+      # Issues
+      issue_1 = create(:issue, title: 'issue_1', project: project)
+      issue_2 = create(:issue, title: 'issue_2', project: project)
+      issue_3 = create(:issue, title: 'issue_3', project: project)
+      issue_4 = create(:issue, title: 'issue_4', project: project)
+      issue_5 = create(:issue, title: 'issue_5', project: project)
+
+      # Assign labels to issues disorderly
+      issue_4.labels << label_1
+      issue_3.labels << label_2
+      issue_5.labels << label_3
+      issue_2.labels << label_4
+      issue_1.labels << label_5
+
+      login_as user
+      visit namespace_project_issues_path(project.namespace, project, sort: 'priority')
+
+      # Ensure we are indicating that issues are sorted by priority
+      expect(page).to have_selector('.dropdown-toggle', text: 'Priority')
+
+      page.within('.issues-holder') do
+        issue_titles = all('.issues-list .issue-title-text').map(&:text)
+
+        expect(issue_titles).to eq(['issue_4', 'issue_3', 'issue_5', 'issue_2', 'issue_1'])
+      end
+    end
+  end
+
+  context 'when issues have multiple labels' do
+    scenario 'Are sorted properly' do
+
+      # Issues
+      issue_1 = create(:issue, title: 'issue_1', project: project)
+      issue_2 = create(:issue, title: 'issue_2', project: project)
+      issue_3 = create(:issue, title: 'issue_3', project: project)
+      issue_4 = create(:issue, title: 'issue_4', project: project)
+      issue_5 = create(:issue, title: 'issue_5', project: project)
+      issue_6 = create(:issue, title: 'issue_6', project: project)
+      issue_7 = create(:issue, title: 'issue_7', project: project)
+      issue_8 = create(:issue, title: 'issue_8', project: project)
+
+      # Assign labels to issues disorderly
+      issue_5.labels << label_1 # 1
+      issue_5.labels << label_2
+      issue_8.labels << label_1 # 2
+      issue_1.labels << label_2 # 3
+      issue_1.labels << label_3
+      issue_3.labels << label_2 # 4
+      issue_3.labels << label_4
+      issue_7.labels << label_2 # 5
+      issue_2.labels << label_3 # 6
+      issue_4.labels << label_4 # 7
+      issue_6.labels << label_5 # 8 - No priority
+
+      login_as user
+      visit namespace_project_issues_path(project.namespace, project, sort: 'priority')
+
+      expect(page).to have_selector('.dropdown-toggle', text: 'Priority')
+
+      page.within('.issues-holder') do
+        issue_titles = all('.issues-list .issue-title-text').map(&:text)
+
+        expect(issue_titles[0..1]).to contain_exactly('issue_5', 'issue_8')
+        expect(issue_titles[2..4]).to contain_exactly('issue_1', 'issue_3', 'issue_7')
+        expect(issue_titles[5..-1]).to eq(['issue_2', 'issue_4', 'issue_6'])
+      end
+    end
+  end
+end
diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8550d279d09728e94f4c45ed9b5d129b6c396dc0
--- /dev/null
+++ b/spec/features/projects/labels/update_prioritization_spec.rb
@@ -0,0 +1,115 @@
+require 'spec_helper'
+
+feature 'Prioritize labels', feature: true do
+  include WaitForAjax
+
+  context 'when project belongs to user' do
+    let(:user)    { create(:user) }
+    let(:project) { create(:project, name: 'test', namespace: user.namespace) }
+
+    scenario 'user can prioritize a label', js: true do
+      bug     = create(:label, title: 'bug')
+      wontfix = create(:label, title: 'wontfix')
+
+      project.labels << bug
+      project.labels << wontfix
+
+      login_as user
+      visit namespace_project_labels_path(project.namespace, project)
+
+      expect(page).to have_content('No prioritized labels yet')
+
+      page.within('.other-labels') do
+        first('.js-toggle-priority').click
+        wait_for_ajax
+        expect(page).not_to have_content('bug')
+      end
+
+      page.within('.prioritized-labels') do
+        expect(page).not_to have_content('No prioritized labels yet')
+        expect(page).to have_content('bug')
+      end
+    end
+
+    scenario 'user can unprioritize a label', js: true do
+      bug     = create(:label, title: 'bug', priority: 1)
+      wontfix = create(:label, title: 'wontfix')
+
+      project.labels << bug
+      project.labels << wontfix
+
+      login_as user
+      visit namespace_project_labels_path(project.namespace, project)
+
+      expect(page).to have_content('bug')
+
+      page.within('.prioritized-labels') do
+        first('.js-toggle-priority').click
+        wait_for_ajax
+        expect(page).not_to have_content('bug')
+      end
+
+      page.within('.other-labels') do
+        expect(page).to have_content('bug')
+        expect(page).to have_content('wontfix')
+      end
+    end
+
+    scenario 'user can sort prioritized labels and persist across reloads', js: true do
+      bug     = create(:label, title: 'bug', priority: 1)
+      wontfix = create(:label, title: 'wontfix', priority: 2)
+
+      project.labels << bug
+      project.labels << wontfix
+
+      login_as user
+      visit namespace_project_labels_path(project.namespace, project)
+
+      expect(page).to have_content 'bug'
+      expect(page).to have_content 'wontfix'
+
+      # Sort labels
+      find("#label_#{bug.id}").drag_to find("#label_#{wontfix.id}")
+
+      page.within('.prioritized-labels') do
+        expect(first('li')).to have_content('wontfix')
+        expect(page.all('li').last).to have_content('bug')
+      end
+
+      visit current_url
+
+      page.within('.prioritized-labels') do
+        expect(first('li')).to have_content('wontfix')
+        expect(page.all('li').last).to have_content('bug')
+      end
+    end
+  end
+
+  context 'as a guest' do
+    it 'can not prioritize labels' do
+      user = create(:user)
+      guest = create(:user)
+      project = create(:project, name: 'test', namespace: user.namespace)
+
+      create(:label, title: 'bug')
+
+      login_as guest
+      visit namespace_project_labels_path(project.namespace, project)
+
+      expect(page).not_to have_css('.prioritized-labels')
+    end
+  end
+
+  context 'as a non signed in user' do
+    it 'can not prioritize labels' do
+      user = create(:user)
+      project = create(:project, name: 'test', namespace: user.namespace)
+
+      create(:label, title: 'bug')
+
+      visit namespace_project_labels_path(project.namespace, project)
+
+      expect(page).not_to have_css('.prioritized-labels')
+    end
+  end
+end
diff --git a/spec/features/projects/members/master_manages_access_requests_spec.rb b/spec/features/projects/members/master_manages_access_requests_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5fe4caa12f07492d1991d041190024debe9578b7
--- /dev/null
+++ b/spec/features/projects/members/master_manages_access_requests_spec.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+
+feature 'Projects > Members > Master manages access requests', feature: true do
+  let(:user) { create(:user) }
+  let(:master) { create(:user) }
+  let(:project) { create(:project, :public) }
+
+  background do
+    project.request_access(user)
+    project.team << [master, :master]
+    login_as(master)
+  end
+
+  scenario 'master can see access requests' do
+    visit namespace_project_project_members_path(project.namespace, project)
+
+    expect_visible_access_request(project, user)
+  end
+
+  scenario 'master can grant access' do
+    visit namespace_project_project_members_path(project.namespace, project)
+
+    expect_visible_access_request(project, user)
+
+    perform_enqueued_jobs { click_on 'Grant access' }
+
+    expect(ActionMailer::Base.deliveries.last.to).to eq [user.notification_email]
+    expect(ActionMailer::Base.deliveries.last.subject).to match "Access to the #{project.name_with_namespace} project was granted"
+  end
+
+  scenario 'master can deny access' do
+    visit namespace_project_project_members_path(project.namespace, project)
+
+    expect_visible_access_request(project, user)
+
+    perform_enqueued_jobs { click_on 'Deny access' }
+
+    expect(ActionMailer::Base.deliveries.last.to).to eq [user.notification_email]
+    expect(ActionMailer::Base.deliveries.last.subject).to match "Access to the #{project.name_with_namespace} project was denied"
+  end
+
+  def expect_visible_access_request(project, user)
+    expect(project.members.request.exists?(user_id: user)).to be_truthy
+    expect(page).to have_content "#{project.name} access requests (1)"
+    expect(page).to have_content user.name
+  end
+end
diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fd92a3a2f0cf20eb61d0dcb6bfd8f5acd305e80a
--- /dev/null
+++ b/spec/features/projects/members/user_requests_access_spec.rb
@@ -0,0 +1,54 @@
+require 'spec_helper'
+
+feature 'Projects > Members > User requests access', feature: true do
+  let(:user) { create(:user) }
+  let(:master) { create(:user) }
+  let(:project) { create(:project, :public) }
+
+  background do
+    project.team << [master, :master]
+    login_as(user)
+    visit namespace_project_path(project.namespace, project)
+  end
+
+  scenario 'user can request access to a project' do
+    perform_enqueued_jobs { click_link 'Request Access' }
+
+    expect(ActionMailer::Base.deliveries.last.to).to eq [master.notification_email]
+    expect(ActionMailer::Base.deliveries.last.subject).to eq "Request to join the #{project.name_with_namespace} project"
+
+    expect(project.members.request.exists?(user_id: user)).to be_truthy
+    expect(page).to have_content 'Your request for access has been queued for review.'
+
+    expect(page).to have_content 'Withdraw Access Request'
+  end
+
+  scenario 'user is not listed in the project members page' do
+    click_link 'Request Access'
+
+    expect(project.members.request.exists?(user_id: user)).to be_truthy
+
+    open_project_settings_menu
+    click_link 'Members'
+
+    visit namespace_project_project_members_path(project.namespace, project)
+    page.within('.content') do
+      expect(page).not_to have_content(user.name)
+    end
+  end
+
+  scenario 'user can withdraw its request for access' do
+    click_link 'Request Access'
+
+    expect(project.members.request.exists?(user_id: user)).to be_truthy
+
+    click_link 'Withdraw Access Request'
+
+    expect(project.members.request.exists?(user_id: user)).to be_falsey
+    expect(page).to have_content 'Your access request to the project has been withdrawn.'
+  end
+
+  def open_project_settings_menu
+    find('#project-settings-button').click
+  end
+end
diff --git a/spec/features/projects/path_locks_spec.rb b/spec/features/projects/path_locks_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f36df3fc46cbbcc3e3e8f5d86cd3e8243196c3f7
--- /dev/null
+++ b/spec/features/projects/path_locks_spec.rb
@@ -0,0 +1,59 @@
+require 'spec_helper'
+
+feature 'Path Locks', feature: true, js: true do
+  let(:user) { create(:user) }
+  let(:project) { create(:project, namespace: user.namespace) }
+  let(:project_tree_path) { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) }
+
+  before do
+    allow_any_instance_of(PathLocksHelper).to receive(:license_allows_file_locks?).and_return(true)
+
+    project.team << [user, :master]
+    login_with(user)
+
+    visit project_tree_path
+  end
+
+  scenario 'Locking folders' do
+    within '.tree-content-holder' do
+      click_link "encoding"
+      click_link "Lock"
+
+      visit project_tree_path
+
+      expect(page).to have_selector('.fa-lock')
+    end
+  end
+
+  scenario 'Locking files' do
+    page_tree = find('.tree-content-holder')
+
+    within page_tree do
+      click_link "VERSION"
+    end
+
+    within '.file-actions' do
+      click_link "Lock"
+    end
+
+    visit project_tree_path
+
+    within page_tree do
+      expect(page).to have_selector('.fa-lock')
+    end
+  end
+
+  scenario 'Managing of lock list' do
+    create :path_lock, path: 'encoding', user: user, project: project
+
+    click_link "Locked Files"
+
+    within '.locks' do
+      expect(page).to have_content('encoding')
+
+      find('.btn-remove').click
+
+      expect(page).not_to have_content('encoding')
+    end
+  end
+end
diff --git a/spec/features/project/shortcuts_spec.rb b/spec/features/projects/shortcuts_spec.rb
similarity index 79%
rename from spec/features/project/shortcuts_spec.rb
rename to spec/features/projects/shortcuts_spec.rb
index 2595c4181e5ed896252e30a6e3c83da1485bed9c..54aa9c66a08cf7ad14b781d4736826fca06398ad 100644
--- a/spec/features/project/shortcuts_spec.rb
+++ b/spec/features/projects/shortcuts_spec.rb
@@ -1,7 +1,7 @@
 require 'spec_helper'
 
 feature 'Project shortcuts', feature: true do
-  let(:project) { create(:project) }
+  let(:project) { create(:project, name: 'Victorialand') }
   let(:user) { create(:user) }
 
   describe 'On a project', js: true do
@@ -14,7 +14,7 @@ feature 'Project shortcuts', feature: true do
     describe 'pressing "i"' do
       it 'redirects to new issue page' do
         find('body').native.send_key('i')
-        expect(page).to have_content('New Issue')
+        expect(page).to have_content('Victorialand')
       end
     end
   end
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index 49156130ad335bde9ae550683741c26f955c152e..a5ed3595b0a441b341f02baf2f06bdb1228cb1e9 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -29,8 +29,8 @@ describe "Runners" do
     end
 
     before do
-      expect(page).to_not have_content(@specific_runner3.display_name)
-      expect(page).to_not have_content(@specific_runner3.display_name)
+      expect(page).not_to have_content(@specific_runner3.display_name)
+      expect(page).not_to have_content(@specific_runner3.display_name)
     end
 
     it "places runners in right places" do
diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb
index 029a11ea43ced6cdea8df610c2ef3d388f8c0424..b9e63a7152c74d7ee76a18606daf43cf8afe97cf 100644
--- a/spec/features/search_spec.rb
+++ b/spec/features/search_spec.rb
@@ -47,4 +47,83 @@ describe "Search", feature: true  do
       expect(page).to have_link(snippet.title)
     end
   end
+
+
+  describe 'Right header search field', feature: true do
+
+    describe 'Search in project page' do
+      before do
+        visit namespace_project_path(project.namespace, project)
+      end
+
+      it 'top right search form is present' do
+        expect(page).to have_selector('#search')
+      end
+
+      it 'top right search form contains location badge' do
+        expect(page).to have_selector('.has-location-badge')
+      end
+
+      context 'clicking the search field', js: true do
+        it 'should show category search dropdown' do
+          page.find('#search').click
+
+          expect(page).to have_selector('.dropdown-header', text: /#{project.name}/i)
+        end
+      end
+
+      context 'click the links in the category search dropdown', js: true do
+
+        before do
+          page.find('#search').click
+        end
+
+        it 'should take user to her issues page when issues assigned is clicked' do
+          find('.dropdown-menu').click_link 'Issues assigned to me'
+          sleep 2
+
+          expect(page).to have_selector('.issues-holder')
+          expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name)
+        end
+
+        it 'should take user to her issues page when issues authored is clicked' do
+          find('.dropdown-menu').click_link "Issues I've created"
+          sleep 2
+
+          expect(page).to have_selector('.issues-holder')
+          expect(find('.js-author-search .dropdown-toggle-text')).to have_content(user.name)
+        end
+
+        it 'should take user to her MR page when MR assigned is clicked' do
+          find('.dropdown-menu').click_link 'Merge requests assigned to me'
+          sleep 2
+
+          expect(page).to have_selector('.merge-requests-holder')
+          expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name)
+        end
+
+        it 'should take user to her MR page when MR authored is clicked' do
+          find('.dropdown-menu').click_link "Merge requests I've created"
+          sleep 2
+
+          expect(page).to have_selector('.merge-requests-holder')
+          expect(find('.js-author-search .dropdown-toggle-text')).to have_content(user.name)
+        end
+      end
+
+      context 'entering text into the search field', js: true do
+        before do
+          page.within '.search-input-wrap' do
+            fill_in "search", with: project.name[0..3]
+          end
+        end
+
+        it 'should not display the category search dropdown' do
+          expect(page).not_to have_selector('.dropdown-header', text: /#{project.name}/i)
+        end
+      end
+    end
+  end
+
+
 end
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index 22614dc8b5ed3db751ffd54859464b6e68ddc79e..ec2bdb5a1f09ba8cad07095273991d3f353af1ef 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -142,8 +142,8 @@ describe "Public Project Access", feature: true  do
   end
 
   describe "GET /:project_path/builds/:id" do
-    let(:commit) { create(:ci_commit, project: project) }
-    let(:build) { create(:ci_build, commit: commit) }
+    let(:pipeline) { create(:ci_pipeline, project: project) }
+    let(:build) { create(:ci_build, pipeline: pipeline) }
     subject { namespace_project_build_path(project.namespace, project, build.id) }
 
     context "when allowed for public" do
@@ -175,6 +175,49 @@ describe "Public Project Access", feature: true  do
     end
   end
 
+  describe "GET /:project_path/environments" do
+    subject { namespace_project_environments_path(project.namespace, project) }
+
+    it { is_expected.to be_allowed_for :admin }
+    it { is_expected.to be_allowed_for owner }
+    it { is_expected.to be_allowed_for master }
+    it { is_expected.to be_allowed_for developer }
+    it { is_expected.to be_allowed_for reporter }
+    it { is_expected.to be_denied_for guest }
+    it { is_expected.to be_denied_for :user }
+    it { is_expected.to be_denied_for :external }
+    it { is_expected.to be_denied_for :visitor }
+  end
+
+  describe "GET /:project_path/environments/:id" do
+    let(:environment) { create(:environment, project: project) }
+    subject { namespace_project_environments_path(project.namespace, project, environment) }
+
+    it { is_expected.to be_allowed_for :admin }
+    it { is_expected.to be_allowed_for owner }
+    it { is_expected.to be_allowed_for master }
+    it { is_expected.to be_allowed_for developer }
+    it { is_expected.to be_allowed_for reporter }
+    it { is_expected.to be_denied_for guest }
+    it { is_expected.to be_denied_for :user }
+    it { is_expected.to be_denied_for :external }
+    it { is_expected.to be_denied_for :visitor }
+  end
+
+  describe "GET /:project_path/environments/new" do
+    subject { new_namespace_project_environment_path(project.namespace, project) }
+
+    it { is_expected.to be_allowed_for :admin }
+    it { is_expected.to be_allowed_for owner }
+    it { is_expected.to be_allowed_for master }
+    it { is_expected.to be_allowed_for developer }
+    it { is_expected.to be_denied_for reporter }
+    it { is_expected.to be_denied_for guest }
+    it { is_expected.to be_denied_for :user }
+    it { is_expected.to be_denied_for :external }
+    it { is_expected.to be_denied_for :visitor }
+  end
+
   describe "GET /:project_path/blob" do
     let(:commit) { project.repository.commit }
 
diff --git a/spec/features/tags/master_updates_tag_spec.rb b/spec/features/tags/master_updates_tag_spec.rb
index c926e9841f31f23fe87ab85b763d4ff6b23bc336..6b5b3122f725279c68ea2c779cbe0bf37914eecf 100644
--- a/spec/features/tags/master_updates_tag_spec.rb
+++ b/spec/features/tags/master_updates_tag_spec.rb
@@ -12,7 +12,7 @@ feature 'Master updates tag', feature: true do
 
   context 'from the tags list page' do
     scenario 'updates the release notes' do
-      page.within(first('.controls')) do
+      page.within(first('.content-list .controls')) do
         click_link 'Edit release notes'
       end
 
diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index b7368cca29d0cdb3dbfcca4f7910718866e10cc7..6ed279ef9be6f3413b683b6c793e72cf7ca6e2ad 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -75,7 +75,10 @@ feature 'Task Lists', feature: true do
 
   describe 'for Notes' do
     let!(:issue) { create(:issue, author: user, project: project) }
-    let!(:note)  { create(:note, note: markdown, noteable: issue, author: user) }
+    let!(:note) do
+      create(:note, note: markdown, noteable: issue,
+                    project: project, author: user)
+    end
 
     it 'renders for note body' do
       visit_issue(project, issue)
diff --git a/spec/features/todos/target_state_spec.rb b/spec/features/todos/target_state_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..32fa88a2b21cbdae47a004d0889439ed29c0b66c
--- /dev/null
+++ b/spec/features/todos/target_state_spec.rb
@@ -0,0 +1,65 @@
+require 'rails_helper'
+
+feature 'Todo target states', feature: true do
+  let(:user)    { create(:user) }
+  let(:author)  { create(:user) }
+  let(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+
+  before do
+    login_as user
+  end
+
+  scenario 'on a closed issue todo has closed label' do
+    issue_closed = create(:issue, state: 'closed')
+    create_todo issue_closed
+    visit dashboard_todos_path
+
+    page.within '.todos-list' do
+      expect(page).to have_content('Closed')
+    end
+  end
+
+  scenario 'on an open issue todo does not have an open label' do
+    issue_open = create(:issue)
+    create_todo issue_open
+    visit dashboard_todos_path
+
+    page.within '.todos-list' do
+      expect(page).not_to have_content('Open')
+    end
+  end
+
+  scenario 'on a merged merge request todo has merged label' do
+    mr_merged = create(:merge_request, :simple, author: user, state: 'merged')
+    create_todo mr_merged
+    visit dashboard_todos_path
+
+    page.within '.todos-list' do
+      expect(page).to have_content('Merged')
+    end
+  end
+
+  scenario 'on a closed merge request todo has closed label' do
+    mr_closed = create(:merge_request, :simple, author: user, state: 'closed')
+    create_todo mr_closed
+    visit dashboard_todos_path
+
+    page.within '.todos-list' do
+      expect(page).to have_content('Closed')
+    end
+  end
+
+  scenario 'on an open merge request todo does not have an open label' do
+    mr_open = create(:merge_request, :simple, author: user)
+    create_todo mr_open
+    visit dashboard_todos_path
+
+    page.within '.todos-list' do
+      expect(page).not_to have_content('Open')
+    end
+  end
+
+  def create_todo(target)
+    create(:todo, :mentioned, user: user, project: project, target: target, author: author)
+  end
+end
diff --git a/spec/features/todos/todos_spec.rb b/spec/features/todos/todos_spec.rb
index 3354f5292956569321dd16dfc9e7fd6934eac54c..8e1833a069ee58c7561aa022344d1c2926ee392e 100644
--- a/spec/features/todos/todos_spec.rb
+++ b/spec/features/todos/todos_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
 describe 'Dashboard Todos', feature: true do
   let(:user)    { create(:user) }
   let(:author)  { create(:user) }
-  let(:project) { create(:project) }
+  let(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
   let(:issue)   { create(:issue) }
 
   describe 'GET /dashboard/todos' do
@@ -43,6 +43,27 @@ describe 'Dashboard Todos', feature: true do
       end
     end
 
+    context 'User has Todos with labels spanning multiple projects' do
+      before do
+        label1 = create(:label, project: project)
+        note1 = create(:note_on_issue, note: "Hello #{label1.to_reference(format: :name)}", noteable_id: issue.id, noteable_type: 'Issue', project: issue.project)
+        create(:todo, :mentioned, project: project, target: issue, user: user, note_id: note1.id)
+
+        project2 = create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+        label2 = create(:label, project: project2)
+        issue2 = create(:issue, project: project2)
+        note2 = create(:note_on_issue, note: "Test #{label2.to_reference(format: :name)}", noteable_id: issue2.id, noteable_type: 'Issue', project: project2)
+        create(:todo, :mentioned, project: project2, target: issue2, user: user, note_id: note2.id)
+
+        login_as(user)
+        visit dashboard_todos_path
+      end
+
+      it 'shows page with two Todos' do
+        expect(page).to have_selector('.todos-list .todo', count: 2)
+      end
+    end
+
     context 'User has multiple pages of Todos' do
       before do
         allow(Todo).to receive(:default_per_page).and_return(1)
@@ -77,5 +98,18 @@ describe 'Dashboard Todos', feature: true do
         end
       end
     end
+
+    context 'User has a Todo in a project pending deletion' do
+      before do
+        deleted_project = create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC, pending_delete: true)
+        create(:todo, :mentioned, user: user, project: deleted_project, target: issue, author: author)
+        login_as(user)
+        visit dashboard_todos_path
+      end
+
+      it 'shows "All done" message' do
+        expect(page).to have_content "You're all done!"
+      end
+    end
   end
 end
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..14613754f746c649e66eee8811bd787bcbf4687d
--- /dev/null
+++ b/spec/features/u2f_spec.rb
@@ -0,0 +1,228 @@
+require 'spec_helper'
+
+feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: true, js: true do
+  def register_u2f_device(u2f_device = nil)
+    u2f_device ||= FakeU2fDevice.new(page)
+    u2f_device.respond_to_u2f_registration
+    click_on 'Setup New U2F Device'
+    expect(page).to have_content('Your device was successfully set up')
+    click_on 'Register U2F Device'
+    u2f_device
+  end
+
+  describe "registration" do
+    let(:user) { create(:user) }
+
+    before do
+      login_as(user)
+      user.update_attribute(:otp_required_for_login, true)
+    end
+
+    describe 'when 2FA via OTP is disabled' do
+      before { user.update_attribute(:otp_required_for_login, false) }
+
+      it 'does not allow registering a new device' do
+        visit profile_account_path
+        click_on 'Enable Two-Factor Authentication'
+
+        expect(page).to have_button('Setup New U2F Device', disabled: true)
+      end
+    end
+
+    describe 'when 2FA via OTP is enabled' do
+      it 'allows registering a new device' do
+        visit profile_account_path
+        click_on 'Manage Two-Factor Authentication'
+        expect(page.body).to match("You've already enabled two-factor authentication using mobile")
+
+        register_u2f_device
+
+        expect(page.body).to match('Your U2F device was registered')
+      end
+
+      it 'allows registering more than one device' do
+        visit profile_account_path
+
+        # First device
+        click_on 'Manage Two-Factor Authentication'
+        register_u2f_device
+        expect(page.body).to match('Your U2F device was registered')
+
+        # Second device
+        click_on 'Manage Two-Factor Authentication'
+        register_u2f_device
+        expect(page.body).to match('Your U2F device was registered')
+        click_on 'Manage Two-Factor Authentication'
+        expect(page.body).to match('You have 2 U2F devices registered')
+      end
+    end
+
+    it 'allows the same device to be registered for multiple users' do
+      # First user
+      visit profile_account_path
+      click_on 'Manage Two-Factor Authentication'
+      u2f_device = register_u2f_device
+      expect(page.body).to match('Your U2F device was registered')
+      logout
+
+      # Second user
+      user = login_as(:user)
+      user.update_attribute(:otp_required_for_login, true)
+      visit profile_account_path
+      click_on 'Manage Two-Factor Authentication'
+      register_u2f_device(u2f_device)
+      expect(page.body).to match('Your U2F device was registered')
+
+      expect(U2fRegistration.count).to eq(2)
+    end
+
+    context "when there are form errors" do
+      it "doesn't register the device if there are errors" do
+        visit profile_account_path
+        click_on 'Manage Two-Factor Authentication'
+
+        # Have the "u2f device" respond with bad data
+        page.execute_script("u2f.register = function(_,_,_,callback) { callback('bad response'); };")
+        click_on 'Setup New U2F Device'
+        expect(page).to have_content('Your device was successfully set up')
+        click_on 'Register U2F Device'
+
+        expect(U2fRegistration.count).to eq(0)
+        expect(page.body).to match("The form contains the following error")
+        expect(page.body).to match("did not send a valid JSON response")
+      end
+
+      it "allows retrying registration" do
+        visit profile_account_path
+        click_on 'Manage Two-Factor Authentication'
+
+        # Failed registration
+        page.execute_script("u2f.register = function(_,_,_,callback) { callback('bad response'); };")
+        click_on 'Setup New U2F Device'
+        expect(page).to have_content('Your device was successfully set up')
+        click_on 'Register U2F Device'
+        expect(page.body).to match("The form contains the following error")
+
+        # Successful registration
+        register_u2f_device
+
+        expect(page.body).to match('Your U2F device was registered')
+        expect(U2fRegistration.count).to eq(1)
+      end
+    end
+  end
+
+  describe "authentication" do
+    let(:user) { create(:user) }
+
+    before do
+      # Register and logout
+      login_as(user)
+      user.update_attribute(:otp_required_for_login, true)
+      visit profile_account_path
+      click_on 'Manage Two-Factor Authentication'
+      @u2f_device = register_u2f_device
+      logout
+    end
+
+    describe "when 2FA via OTP is disabled" do
+      it "allows logging in with the U2F device" do
+        login_with(user)
+
+        @u2f_device.respond_to_u2f_authentication
+        click_on "Login Via U2F Device"
+        expect(page.body).to match('We heard back from your U2F device')
+        click_on "Authenticate via U2F Device"
+
+        expect(page.body).to match('Signed in successfully')
+      end
+    end
+
+    describe "when 2FA via OTP is enabled" do
+      it "allows logging in with the U2F device" do
+        user.update_attribute(:otp_required_for_login, true)
+        login_with(user)
+
+        @u2f_device.respond_to_u2f_authentication
+        click_on "Login Via U2F Device"
+        expect(page.body).to match('We heard back from your U2F device')
+        click_on "Authenticate via U2F Device"
+
+        expect(page.body).to match('Signed in successfully')
+      end
+    end
+
+    describe "when a given U2F device has already been registered by another user" do
+      describe "but not the current user" do
+        it "does not allow logging in with that particular device" do
+          # Register current user with the different U2F device
+          current_user = login_as(:user)
+          current_user.update_attribute(:otp_required_for_login, true)
+          visit profile_account_path
+          click_on 'Manage Two-Factor Authentication'
+          register_u2f_device
+          logout
+
+          # Try authenticating user with the old U2F device
+          login_as(current_user)
+          @u2f_device.respond_to_u2f_authentication
+          click_on "Login Via U2F Device"
+          expect(page.body).to match('We heard back from your U2F device')
+          click_on "Authenticate via U2F Device"
+
+          expect(page.body).to match('Authentication via U2F device failed')
+        end
+      end
+
+      describe "and also the current user" do
+        it "allows logging in with that particular device" do
+          # Register current user with the same U2F device
+          current_user = login_as(:user)
+          current_user.update_attribute(:otp_required_for_login, true)
+          visit profile_account_path
+          click_on 'Manage Two-Factor Authentication'
+          register_u2f_device(@u2f_device)
+          logout
+
+          # Try authenticating user with the same U2F device
+          login_as(current_user)
+          @u2f_device.respond_to_u2f_authentication
+          click_on "Login Via U2F Device"
+          expect(page.body).to match('We heard back from your U2F device')
+          click_on "Authenticate via U2F Device"
+
+          expect(page.body).to match('Signed in successfully')
+        end
+      end
+    end
+
+    describe "when a given U2F device has not been registered" do
+      it "does not allow logging in with that particular device" do
+        unregistered_device = FakeU2fDevice.new(page)
+        login_as(user)
+        unregistered_device.respond_to_u2f_authentication
+        click_on "Login Via U2F Device"
+        expect(page.body).to match('We heard back from your U2F device')
+        click_on "Authenticate via U2F Device"
+
+        expect(page.body).to match('Authentication via U2F device failed')
+      end
+    end
+  end
+
+  describe "when two-factor authentication is disabled" do
+    let(:user) { create(:user) }
+
+    before do
+      login_as(user)
+      user.update_attribute(:otp_required_for_login, true)
+      visit profile_account_path
+      click_on 'Manage Two-Factor Authentication'
+      register_u2f_device
+    end
+
+    it "deletes u2f registrations" do
+      expect { click_on "Disable" }.to change { U2fRegistration.count }.from(1).to(0)
+    end
+  end
+end
diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb
index 48e2dae4884063df6c5909f2a5c07f382c74ebe1..a2b8f7b6931ffb5c9786a48b37aecc633b4ed09e 100644
--- a/spec/features/variables_spec.rb
+++ b/spec/features/variables_spec.rb
@@ -34,7 +34,7 @@ describe 'Project variables', js: true do
       find('.btn-variable-delete').click
     end
 
-    expect(page).to_not have_selector('variables-table')
+    expect(page).not_to have_selector('variables-table')
   end
 
   it 'should edit variable' do
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index ec8809e6926c3940f5dba90bc6d6dceaaba2f202..4c70aa7dc7e87de2a9cf7d7cd23cd056c815c18a 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -34,6 +34,22 @@ describe IssuesFinder do
         expect(issues).to contain_exactly(issue1, issue2, issue3)
       end
 
+      context 'sort by issues with no weight' do
+        let(:params) { { weight: Issue::WEIGHT_NONE } }
+
+        it 'returns all issues' do
+          expect(issues).to contain_exactly(issue1, issue2, issue3)
+        end
+      end
+
+      context 'sort by issues with any weight' do
+        let(:params) { { weight: Issue::WEIGHT_ANY } }
+
+        it 'returns all issues' do
+          expect(issues).to be_empty
+        end
+      end
+
       context 'filtering by assignee ID' do
         let(:params) { { assignee_id: user.id } }
 
diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb
index bc385fd0d691c68820214a50d0c9ec293d46f9c8..eda9b9a50154d8873c93e236a532826bfc6af793 100644
--- a/spec/finders/merge_requests_finder_spec.rb
+++ b/spec/finders/merge_requests_finder_spec.rb
@@ -29,5 +29,11 @@ describe MergeRequestsFinder do
       merge_requests = MergeRequestsFinder.new(user, params).execute
       expect(merge_requests.size).to eq(1)
     end
+
+    it 'should ignore sorting by weight' do
+      params = { project_id: project1.id, scope: 'authored', state: 'opened', weight: Issue::WEIGHT_ANY }
+      merge_requests = MergeRequestsFinder.new(user, params).execute
+      expect(merge_requests.size).to eq(1)
+    end
   end
 end
diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb
index c83824b900de0678ce22ebbe70be1bc237005113..639b28d49eedd49b4d78acc4d45832b25c0d4647 100644
--- a/spec/finders/notes_finder_spec.rb
+++ b/spec/finders/notes_finder_spec.rb
@@ -34,5 +34,21 @@ describe NotesFinder do
       notes = NotesFinder.new.execute(project, user, params)
       expect(notes).to eq([note1])
     end
+
+    context 'confidential issue notes' do
+      let(:confidential_issue) { create(:issue, :confidential, project: project, author: user) }
+      let!(:confidential_note) { create(:note, noteable: confidential_issue, project: confidential_issue.project) }
+
+      let(:params) { { target_id: confidential_issue.id, target_type: 'issue', last_fetched_at: 1.hour.ago.to_i } }
+
+      it 'returns notes if user can see the issue' do
+        expect(NotesFinder.new.execute(project, user, params)).to eq([confidential_note])
+      end
+
+      it 'raises an error if user can not see the issue' do
+        user = create(:user)
+        expect { NotesFinder.new.execute(project, user, params) }.to raise_error(ActiveRecord::RecordNotFound)
+      end
+    end
   end
 end
diff --git a/spec/fixtures/container_registry/tag_manifest_1.json b/spec/fixtures/container_registry/tag_manifest_1.json
new file mode 100644
index 0000000000000000000000000000000000000000..d09ede5bea7e4720ada2343238609cd159bb6a25
--- /dev/null
+++ b/spec/fixtures/container_registry/tag_manifest_1.json
@@ -0,0 +1,32 @@
+{
+  "schemaVersion": 1,
+  "name": "library/alpine",
+  "tag": "2.6",
+  "architecture": "amd64",
+  "fsLayers": [
+    {
+      "blobSum": "sha256:2a3ebcb7fbcc29bf40c4f62863008bb573acdea963454834d9483b3e5300c45d"
+    }
+  ],
+  "history": [
+    {
+      "v1Compatibility": "{\"id\":\"dd807873c9a21bcc82e30317c283e6601d7e19f5cf7867eec34cdd1aeb3f099e\",\"created\":\"2016-01-18T18:32:39.162138276Z\",\"container\":\"556a728876db7b0e621adc029c87c649d32520804f8f15defd67bb070dc1a88d\",\"container_config\":{\"Hostname\":\"556a728876db\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":null,\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) ADD file:7dee8a455bcc39013aa168d27ece9227aad155adbaacbd153d94ca60113f59fc in /\"],\"Image\":\"\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":null},\"docker_version\":\"1.8.3\",\"config\":{\"Hostname\":\"556a728876db\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":null,\"Cmd\":null,\"Image\":\"\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":null},\"architecture\":\"amd64\",\"os\":\"linux\",\"Size\":4501436}"
+    }
+  ],
+  "signatures": [
+    {
+      "header": {
+        "jwk": {
+          "crv": "P-256",
+          "kid": "4MZL:Z5ZP:2RPA:Q3TD:QOHA:743L:EM2G:QY6Q:ZJCX:BSD7:CRYC:LQ6T",
+          "kty": "EC",
+          "x": "qmWOaxPUk7QsE5iTPdeG1e9yNE-wranvQEnWzz9FhWM",
+          "y": "WeeBpjTOYnTNrfCIxtFY5qMrJNNk9C1vc5ryxbbMD_M"
+        },
+        "alg": "ES256"
+      },
+      "signature": "0zmjTJ4m21yVwAeteLc3SsQ0miScViCDktFPR67W-ozGjjI3iBjlDjwOl6o2sds5ZI9U6bSIKOeLDinGOhHoOQ",
+      "protected": "eyJmb3JtYXRMZW5ndGgiOjEzNzIsImZvcm1hdFRhaWwiOiJDbjAiLCJ0aW1lIjoiMjAxNi0wNi0xNVQxMDo0NDoxNFoifQ"
+    }
+  ]
+}
diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb
index 34ce7c4f0334281fc39d137bb967fae3f1e48db2..c75d28d98012710e6e3c642d8b4611e35b1db363 100644
--- a/spec/fixtures/markdown.md.erb
+++ b/spec/fixtures/markdown.md.erb
@@ -136,7 +136,7 @@ But it shouldn't autolink text inside certain tags:
 
 ### ExternalLinkFilter
 
-External links get a `rel="nofollow"` attribute:
+External links get a `rel="nofollow noreferrer"` and `target="_blank"` attributes:
 
 - [Google](https://google.com/)
 - [GitLab Root](<%= Gitlab.config.gitlab.url %>)
diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb
index 16fbb5dcecb1c023cfbf7a4e4cd868eb939211f6..49ea4fa6d3e18c1c508f831ed8b1a40a813a54d4 100644
--- a/spec/helpers/auth_helper_spec.rb
+++ b/spec/helpers/auth_helper_spec.rb
@@ -36,7 +36,7 @@ describe AuthHelper do
         )
 
         expect(helper.enabled_button_based_providers).to include('twitter')
-        expect(helper.enabled_button_based_providers).to_not include('github')
+        expect(helper.enabled_button_based_providers).not_to include('github')
       end
     end
   end
diff --git a/spec/helpers/ci_status_helper_spec.rb b/spec/helpers/ci_status_helper_spec.rb
index f942695b6f0f5b24104de6baa68928448563a8f9..45199d0f09da61489db4f2d3ed95452faa654605 100644
--- a/spec/helpers/ci_status_helper_spec.rb
+++ b/spec/helpers/ci_status_helper_spec.rb
@@ -3,8 +3,8 @@ require 'spec_helper'
 describe CiStatusHelper do
   include IconsHelper
 
-  let(:success_commit) { double("Ci::Commit", status: 'success') }
-  let(:failed_commit) { double("Ci::Commit", status: 'failed') }
+  let(:success_commit) { double("Ci::Pipeline", status: 'success') }
+  let(:failed_commit) { double("Ci::Pipeline", status: 'failed') }
 
   describe 'ci_icon_for_status' do
     it { expect(helper.ci_icon_for_status(success_commit.status)).to include('fa-check') }
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index 13de88e2f21e475ab577fd6e5036e945a38500af..ade5c3b02d93660f9ef8eb9d39d3d31f00334e63 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -121,13 +121,14 @@ describe GitlabMarkdownHelper do
     before do
       @wiki = double('WikiPage')
       allow(@wiki).to receive(:content).and_return('wiki content')
+      allow(@wiki).to receive(:slug).and_return('nested/page')
       helper.instance_variable_set(:@project_wiki, @wiki)
     end
 
     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', pipeline: :wiki, project_wiki: @wiki)
+      expect(helper).to receive(:markdown).with('wiki content', pipeline: :wiki, project_wiki: @wiki, page_slug: "nested/page")
 
       helper.render_wiki_content(@wiki)
     end
diff --git a/spec/helpers/gitlab_routing_helper_spec.rb b/spec/helpers/gitlab_routing_helper_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..14847d0a49e584e630cf21e9d6937d2ba41baf6f
--- /dev/null
+++ b/spec/helpers/gitlab_routing_helper_spec.rb
@@ -0,0 +1,79 @@
+require 'spec_helper'
+
+describe GitlabRoutingHelper do
+  describe 'Project URL helpers' do
+    describe '#project_members_url' do
+      let(:project) { build_stubbed(:empty_project) }
+
+      it { expect(project_members_url(project)).to eq namespace_project_project_members_url(project.namespace, project) }
+    end
+
+    describe '#project_member_path' do
+      let(:project_member) { create(:project_member) }
+
+      it { expect(project_member_path(project_member)).to eq namespace_project_project_member_path(project_member.source.namespace, project_member.source, project_member) }
+    end
+
+    describe '#request_access_project_members_path' do
+      let(:project) { build_stubbed(:empty_project) }
+
+      it { expect(request_access_project_members_path(project)).to eq request_access_namespace_project_project_members_path(project.namespace, project) }
+    end
+
+    describe '#leave_project_members_path' do
+      let(:project) { build_stubbed(:empty_project) }
+
+      it { expect(leave_project_members_path(project)).to eq leave_namespace_project_project_members_path(project.namespace, project) }
+    end
+
+    describe '#approve_access_request_project_member_path' do
+      let(:project_member) { create(:project_member) }
+
+      it { expect(approve_access_request_project_member_path(project_member)).to eq approve_access_request_namespace_project_project_member_path(project_member.source.namespace, project_member.source, project_member) }
+    end
+
+    describe '#resend_invite_project_member_path' do
+      let(:project_member) { create(:project_member) }
+
+      it { expect(resend_invite_project_member_path(project_member)).to eq resend_invite_namespace_project_project_member_path(project_member.source.namespace, project_member.source, project_member) }
+    end
+  end
+
+  describe 'Group URL helpers' do
+    describe '#group_members_url' do
+      let(:group) { build_stubbed(:group) }
+
+      it { expect(group_members_url(group)).to eq group_group_members_url(group) }
+    end
+
+    describe '#group_member_path' do
+      let(:group_member) { create(:group_member) }
+
+      it { expect(group_member_path(group_member)).to eq group_group_member_path(group_member.source, group_member) }
+    end
+
+    describe '#request_access_group_members_path' do
+      let(:group) { build_stubbed(:group) }
+
+      it { expect(request_access_group_members_path(group)).to eq request_access_group_group_members_path(group) }
+    end
+
+    describe '#leave_group_members_path' do
+      let(:group) { build_stubbed(:group) }
+
+      it { expect(leave_group_members_path(group)).to eq leave_group_group_members_path(group) }
+    end
+
+    describe '#approve_access_request_group_member_path' do
+      let(:group_member) { create(:group_member) }
+
+      it { expect(approve_access_request_group_member_path(group_member)).to eq approve_access_request_group_group_member_path(group_member.source, group_member) }
+    end
+
+    describe '#resend_invite_group_member_path' do
+      let(:group_member) { create(:group_member) }
+
+      it { expect(resend_invite_group_member_path(group_member)).to eq resend_invite_group_group_member_path(group_member.source, group_member) }
+    end
+  end
+end
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index bffe2c18b6f90d86d801d3455ae13a8a466ab195..831ae7fb69c51daa7c51fe82e7c5507bf5e989c2 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -7,10 +7,7 @@ describe IssuesHelper do
 
   describe "url_for_project_issues" do
     let(:project_url) { ext_project.external_issue_tracker.project_url }
-    let(:ext_expected) do
-      project_url.gsub(':project_id', ext_project.id.to_s)
-                 .gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
-    end
+    let(:ext_expected) { project_url.gsub(':project_id', ext_project.id.to_s) }
     let(:int_expected) { polymorphic_path([@project.namespace, project]) }
 
     it "should return internal path if used internal tracker" do
@@ -56,11 +53,7 @@ describe IssuesHelper do
 
   describe "url_for_issue" do
     let(:issues_url) { ext_project.external_issue_tracker.issues_url}
-    let(:ext_expected) do
-      issues_url.gsub(':id', issue.iid.to_s)
-        .gsub(':project_id', ext_project.id.to_s)
-        .gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
-    end
+    let(:ext_expected) { issues_url.gsub(':id', issue.iid.to_s).gsub(':project_id', ext_project.id.to_s) }
     let(:int_expected) { polymorphic_path([@project.namespace, project, issue]) }
 
     it "should return internal path if used internal tracker" do
@@ -106,10 +99,7 @@ describe IssuesHelper do
 
   describe 'url_for_new_issue' do
     let(:issues_url) { ext_project.external_issue_tracker.new_issue_url }
-    let(:ext_expected) do
-      issues_url.gsub(':project_id', ext_project.id.to_s)
-        .gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
-    end
+    let(:ext_expected) { issues_url.gsub(':project_id', ext_project.id.to_s) }
     let(:int_expected) { new_namespace_project_issue_path(project.namespace, project) }
 
     it "should return internal path if used internal tracker" do
@@ -163,18 +153,15 @@ describe IssuesHelper do
     it { is_expected.to eq("!1, !2, or !3") }
   end
 
-  describe "note_active_class" do
-    before do
-      @note = create :note
-      @note1 = create :note
-    end
+  describe '#award_active_class' do
+    let!(:upvote) { create(:award_emoji) }
 
     it "returns empty string for unauthenticated user" do
-      expect(note_active_class(Note.all, nil)).to eq("")
+      expect(award_active_class(AwardEmoji.all, nil)).to eq("")
     end
 
     it "returns active string for author" do
-      expect(note_active_class(Note.all, @note.author)).to eq("active")
+      expect(award_active_class(AwardEmoji.all, upvote.user)).to eq("active")
     end
   end
 
diff --git a/spec/helpers/license_helper_spec.rb b/spec/helpers/license_helper_spec.rb
index 49786da54d72cb2fdf0786b41d871b618e76c4ae..f2a94a79cd2759d4fdee0bcd4a314d5f4125929d 100644
--- a/spec/helpers/license_helper_spec.rb
+++ b/spec/helpers/license_helper_spec.rb
@@ -19,25 +19,5 @@ describe LicenseHelper do
         expect(license_message(signed_in: true, is_admin: false)).to eq(user_msg)
       end
     end
-
-    context 'license available' do
-      let(:license) { create(:license) }
-
-      before do
-        allow(License).to receive(:current).and_return(license)
-      end
-
-      it 'warn for overusage' do
-        allow(license).to receive(:starts_at).and_return(Time.now - 3.months)
-        allow(license).to receive(:expired?).and_return(false)
-        allow(license).to receive(:restricted?).and_return(true)
-        allow(license).to receive(:notify_admins?).and_return(true)
-        allow(license).to receive(:restrictions).and_return({ active_user_count: 50 })
-        allow(User).to receive(:active).and_return(Array.new(100))
-
-        warn_msg = 'Your GitLab license currently covers 50 users, but it looks like your site has grown to 100 users. Please contact sales@gitlab.com to increase the number of licensed users. Note: This message is only visible to you as an admin.'
-        expect(license_message(signed_in: true, is_admin: true)).to eq(warn_msg)
-      end
-    end
   end
 end
diff --git a/spec/helpers/members_helper_spec.rb b/spec/helpers/members_helper_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0b1a76156e033756c86144a23b8e7748a3a1528a
--- /dev/null
+++ b/spec/helpers/members_helper_spec.rb
@@ -0,0 +1,72 @@
+require 'spec_helper'
+
+describe MembersHelper do
+  describe '#action_member_permission' do
+    let(:project_member) { build(:project_member) }
+    let(:group_member) { build(:group_member) }
+
+    it { expect(action_member_permission(:admin, project_member)).to eq :admin_project_member }
+    it { expect(action_member_permission(:admin, group_member)).to eq :admin_group_member }
+  end
+
+  describe '#can_see_member_roles?' do
+    let(:project) { create(:empty_project) }
+    let(:group) { create(:group) }
+    let(:user) { build(:user) }
+    let(:admin) { build(:user, :admin) }
+    let(:project_member) { create(:project_member, project: project) }
+    let(:group_member) { create(:group_member, group: group) }
+
+    it { expect(can_see_member_roles?(source: project, user: nil)).to be_falsy }
+    it { expect(can_see_member_roles?(source: group, user: nil)).to be_falsy }
+    it { expect(can_see_member_roles?(source: project, user: admin)).to be_truthy }
+    it { expect(can_see_member_roles?(source: group, user: admin)).to be_truthy }
+    it { expect(can_see_member_roles?(source: project, user: project_member.user)).to be_truthy }
+    it { expect(can_see_member_roles?(source: group, user: group_member.user)).to be_truthy }
+  end
+
+  describe '#remove_member_message' do
+    let(:requester) { build(:user) }
+    let(:project) { create(:project) }
+    let(:project_member) { build(:project_member, project: project) }
+    let(:project_member_invite) { build(:project_member, project: project).tap { |m| m.generate_invite_token! } }
+    let(:project_member_request) { project.request_access(requester) }
+    let(:group) { create(:group) }
+    let(:group_member) { build(:group_member, group: group) }
+    let(:group_member_invite) { build(:group_member, group: group).tap { |m| m.generate_invite_token! } }
+    let(:group_member_request) { group.request_access(requester) }
+
+    it { expect(remove_member_message(project_member)).to eq "Are you sure you want to remove #{project_member.user.name} from the #{project.name_with_namespace} project?" }
+    it { expect(remove_member_message(project_member_invite)).to eq "Are you sure you want to revoke the invitation for #{project_member_invite.invite_email} to join the #{project.name_with_namespace} project?" }
+    it { expect(remove_member_message(project_member_request)).to eq "Are you sure you want to deny #{requester.name}'s request to join the #{project.name_with_namespace} project?" }
+    it { expect(remove_member_message(project_member_request, user: requester)).to eq "Are you sure you want to withdraw your access request for the #{project.name_with_namespace} project?" }
+    it { expect(remove_member_message(group_member)).to eq "Are you sure you want to remove #{group_member.user.name} from the #{group.name} group?" }
+    it { expect(remove_member_message(group_member_invite)).to eq "Are you sure you want to revoke the invitation for #{group_member_invite.invite_email} to join the #{group.name} group?" }
+    it { expect(remove_member_message(group_member_request)).to eq "Are you sure you want to deny #{requester.name}'s request to join the #{group.name} group?" }
+    it { expect(remove_member_message(group_member_request, user: requester)).to eq "Are you sure you want to withdraw your access request for the #{group.name} group?" }
+  end
+
+  describe '#remove_member_title' do
+    let(:requester) { build(:user) }
+    let(:project) { create(:project) }
+    let(:project_member) { build(:project_member, project: project) }
+    let(:project_member_request) { project.request_access(requester) }
+    let(:group) { create(:group) }
+    let(:group_member) { build(:group_member, group: group) }
+    let(:group_member_request) { group.request_access(requester) }
+
+    it { expect(remove_member_title(project_member)).to eq 'Remove user from project' }
+    it { expect(remove_member_title(project_member_request)).to eq 'Deny access request from project' }
+    it { expect(remove_member_title(group_member)).to eq 'Remove user from group' }
+    it { expect(remove_member_title(group_member_request)).to eq 'Deny access request from group' }
+  end
+
+  describe '#leave_confirmation_message' do
+    let(:project) { build_stubbed(:project) }
+    let(:group) { build_stubbed(:group) }
+    let(:user) { build_stubbed(:user) }
+
+    it { expect(leave_confirmation_message(project)).to eq "Are you sure you want to leave the \"#{project.name_with_namespace}\" project?" }
+    it { expect(leave_confirmation_message(group)).to eq "Are you sure you want to leave the \"#{group.name}\" group?" }
+  end
+end
diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb
index 600e1c4e9ecf1727403f68d7cca18bb270be25d7..a3336c87173df31efb333d4c932322cdf49e5ddf 100644
--- a/spec/helpers/merge_requests_helper_spec.rb
+++ b/spec/helpers/merge_requests_helper_spec.rb
@@ -5,7 +5,7 @@ describe MergeRequestsHelper do
     let(:project) { create :project }
     let(:merge_request) { MergeRequest.new }
     let(:ci_service) { CiService.new }
-    let(:last_commit) { Ci::Commit.new({}) }
+    let(:last_commit) { Ci::Pipeline.new({}) }
 
     before do
       allow(merge_request).to receive(:source_project).and_return(project)
@@ -17,7 +17,7 @@ describe MergeRequestsHelper do
     it 'does not include api credentials in a link' do
       allow(ci_service).
         to receive(:build_page).and_return("http://secretuser:secretpass@jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c")
-      expect(helper.ci_build_details_path(merge_request)).to_not match("secret")
+      expect(helper.ci_build_details_path(merge_request)).not_to match("secret")
     end
   end
 
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 6aaf1e685719266b2534bad5823bcc850672928b..4212260c0ac69aa8be5b1b137f5ac16bdf25dc06 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -45,16 +45,6 @@ describe ProjectsHelper do
     end
   end
 
-  describe 'user_max_access_in_project' do
-    let(:project) { create(:project) }
-    let(:user) { create(:user) }
-    before do
-      project.team.add_user(user, Gitlab::Access::MASTER)
-    end
-
-    it { expect(helper.user_max_access_in_project(user.id, project)).to eq('Master') }
-  end
-
   describe "readme_cache_key" do
     let(:project) { create(:project) }
 
diff --git a/spec/helpers/tree_helper_spec.rb b/spec/helpers/tree_helper_spec.rb
index c70dd8076e0e6c79c5049ea53ebb2443ef598629..dd5df420df3a3ae780d5e8378d79fb0aa5cdf3d3 100644
--- a/spec/helpers/tree_helper_spec.rb
+++ b/spec/helpers/tree_helper_spec.rb
@@ -25,4 +25,24 @@ describe TreeHelper do
       end
     end
   end
+
+  describe '#lock_file_link' do
+    let(:path_lock) { create :path_lock }
+    let(:path) { path_lock.path }
+    let(:user) { path_lock.user }
+    let(:project) { path_lock.project }
+
+    it "renders unlock link" do
+      allow(helper).to receive(:can?).and_return(true)
+      allow(helper).to receive(:license_allows_file_locks?).and_return(true)
+      expect(helper.lock_file_link(project, path)).to match('Unlock')
+    end
+
+    it "renders lock link" do
+      allow(helper).to receive(:can?).and_return(true)
+      allow(helper).to receive(:current_user).and_return(user)
+      allow(helper).to receive(:license_allows_file_locks?).and_return(true)
+      expect(helper.lock_file_link(project, 'app/controller')).to match('Lock')
+    end
+  end
 end
diff --git a/spec/javascripts/application_spec.js.coffee b/spec/javascripts/application_spec.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..8af39c41f2f5a93d718050ad83e38973c37dad89
--- /dev/null
+++ b/spec/javascripts/application_spec.js.coffee
@@ -0,0 +1,30 @@
+#= require lib/common_utils
+
+describe 'Application', ->
+  describe 'disable buttons', ->
+    fixture.preload('application.html')
+
+    beforeEach ->
+      fixture.load('application.html')
+
+    it 'should prevent default action for disabled buttons', ->
+
+      gl.utils.preventDisabledButtons()
+
+      isClicked = false
+      $button   = $ '#test-button'
+
+      $button.click -> isClicked = true
+      $button.trigger 'click'
+
+      expect(isClicked).toBe false
+
+
+    it 'should be on the same page if a disabled link clicked', ->
+
+      locationBeforeLinkClick = window.location.href
+      gl.utils.preventDisabledButtons()
+
+      $('#test-link').click()
+
+      expect(window.location.href).toBe locationBeforeLinkClick
diff --git a/spec/javascripts/awards_handler_spec.js.coffee b/spec/javascripts/awards_handler_spec.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..ba191199dc7176102ce472cffbbb9496535d96a1
--- /dev/null
+++ b/spec/javascripts/awards_handler_spec.js.coffee
@@ -0,0 +1,201 @@
+#= require awards_handler
+#= require jquery
+#= require jquery.cookie
+#= require ./fixtures/emoji_menu
+
+awardsHandler      = null
+window.gl        or= {}
+window.gon       or= {}
+gl.emojiAliases    = -> return { '+1': 'thumbsup', '-1': 'thumbsdown' }
+gon.award_menu_url = '/emojis'
+
+
+lazyAssert = (done, assertFn) ->
+
+  setTimeout -> # Maybe jasmine.clock here?
+    assertFn()
+    done()
+  , 333
+
+
+describe 'AwardsHandler', ->
+
+  fixture.preload 'awards_handler.html'
+
+  beforeEach ->
+    fixture.load 'awards_handler.html'
+    awardsHandler = new AwardsHandler
+    spyOn(awardsHandler, 'postEmoji').and.callFake (url, emoji, cb) => cb()
+    spyOn(jQuery, 'get').and.callFake (req, cb) -> cb window.emojiMenu
+
+
+  describe '::showEmojiMenu', ->
+
+    it 'should show emoji menu when Add emoji button clicked', (done) ->
+
+      $('.js-add-award').eq(0).click()
+
+      lazyAssert done, ->
+        $emojiMenu = $ '.emoji-menu'
+        expect($emojiMenu.length).toBe 1
+        expect($emojiMenu.hasClass('is-visible')).toBe yes
+        expect($emojiMenu.find('#emoji_search').length).toBe 1
+        expect($('.js-awards-block.current').length).toBe 1
+
+
+    it 'should also show emoji menu for the smiley icon in notes', (done) ->
+
+      $('.note-action-button').click()
+
+      lazyAssert done, ->
+        $emojiMenu = $ '.emoji-menu'
+        expect($emojiMenu.length).toBe 1
+
+
+    it 'should remove emoji menu when body is clicked', (done) ->
+
+      $('.js-add-award').eq(0).click()
+
+      lazyAssert done, ->
+        $emojiMenu = $('.emoji-menu')
+        $('body').click()
+        expect($emojiMenu.length).toBe 1
+        expect($emojiMenu.hasClass('is-visible')).toBe no
+        expect($('.js-awards-block.current').length).toBe 0
+
+
+  describe '::addAwardToEmojiBar', ->
+
+    it 'should add emoji to votes block', ->
+
+      $votesBlock = $('.js-awards-block').eq 0
+      awardsHandler.addAwardToEmojiBar $votesBlock, 'heart', no
+
+      $emojiButton = $votesBlock.find '[data-emoji=heart]'
+
+      expect($emojiButton.length).toBe 1
+      expect($emojiButton.next('.js-counter').text()).toBe '1'
+      expect($votesBlock.hasClass('hidden')).toBe no
+
+
+    it 'should remove the emoji when we click again', ->
+
+      $votesBlock = $('.js-awards-block').eq 0
+      awardsHandler.addAwardToEmojiBar $votesBlock, 'heart', no
+      awardsHandler.addAwardToEmojiBar $votesBlock, 'heart', no
+      $emojiButton = $votesBlock.find '[data-emoji=heart]'
+
+      expect($emojiButton.length).toBe 0
+
+
+    it 'should decrement the emoji counter', ->
+
+      $votesBlock = $('.js-awards-block').eq 0
+      awardsHandler.addAwardToEmojiBar $votesBlock, 'heart', no
+
+      $emojiButton = $votesBlock.find '[data-emoji=heart]'
+      $emojiButton.next('.js-counter').text 5
+
+      awardsHandler.addAwardToEmojiBar $votesBlock, 'heart', no
+
+      expect($emojiButton.length).toBe 1
+      expect($emojiButton.next('.js-counter').text()).toBe '4'
+
+
+  describe '::getAwardUrl', ->
+
+    it 'should return the url for request', ->
+
+      expect(awardsHandler.getAwardUrl()).toBe '/gitlab-org/gitlab-test/issues/8/toggle_award_emoji'
+
+
+  describe '::addAward and ::checkMutuality', ->
+
+    it 'should handle :+1: and :-1: mutuality', ->
+
+      awardUrl         = awardsHandler.getAwardUrl()
+      $votesBlock      = $('.js-awards-block').eq 0
+      $thumbsUpEmoji   = $votesBlock.find('[data-emoji=thumbsup]').parent()
+      $thumbsDownEmoji = $votesBlock.find('[data-emoji=thumbsdown]').parent()
+
+      awardsHandler.addAward $votesBlock, awardUrl, 'thumbsup', no
+
+      expect($thumbsUpEmoji.hasClass('active')).toBe yes
+      expect($thumbsDownEmoji.hasClass('active')).toBe no
+
+      $thumbsUpEmoji.tooltip()
+      $thumbsDownEmoji.tooltip()
+
+      awardsHandler.addAward $votesBlock, awardUrl, 'thumbsdown', yes
+
+      expect($thumbsUpEmoji.hasClass('active')).toBe no
+      expect($thumbsDownEmoji.hasClass('active')).toBe yes
+
+
+  describe '::removeEmoji', ->
+
+    it 'should remove emoji', ->
+
+      awardUrl    = awardsHandler.getAwardUrl()
+      $votesBlock = $('.js-awards-block').eq 0
+
+      awardsHandler.addAward $votesBlock, awardUrl, 'fire',  no
+      expect($votesBlock.find('[data-emoji=fire]').length).toBe  1
+
+      awardsHandler.removeEmoji $votesBlock.find('[data-emoji=fire]').closest('button')
+      expect($votesBlock.find('[data-emoji=fire]').length).toBe  0
+
+
+  describe 'search', ->
+
+    it 'should filter the emoji', ->
+
+      $('.js-add-award').eq(0).click()
+
+      expect($('[data-emoji=angel]').is(':visible')).toBe yes
+      expect($('[data-emoji=anger]').is(':visible')).toBe yes
+
+      $('#emoji_search').val('ali').trigger 'keyup'
+
+      expect($('[data-emoji=angel]').is(':visible')).toBe no
+      expect($('[data-emoji=anger]').is(':visible')).toBe no
+      expect($('[data-emoji=alien]').is(':visible')).toBe yes
+      expect($('h5.emoji-search').is(':visible')).toBe yes
+
+
+  describe 'emoji menu', ->
+
+    selector = '[data-emoji=sunglasses]'
+
+    openEmojiMenuAndAddEmoji = ->
+
+      $('.js-add-award').eq(0).click()
+
+      $menu  = $ '.emoji-menu'
+      $block = $ '.js-awards-block'
+      $emoji = $menu.find ".emoji-menu-list-item #{selector}"
+
+      expect($emoji.length).toBe 1
+      expect($block.find(selector).length).toBe 0
+
+      $emoji.click()
+
+      expect($menu.hasClass('.is-visible')).toBe no
+      expect($block.find(selector).length).toBe 1
+
+
+    it 'should add selected emoji to awards block', ->
+
+      openEmojiMenuAndAddEmoji()
+
+
+    it 'should remove already selected emoji', ->
+
+      openEmojiMenuAndAddEmoji()
+      $('.js-add-award').eq(0).click()
+
+      $block = $ '.js-awards-block'
+      $emoji = $('.emoji-menu').find ".emoji-menu-list-item #{selector}"
+
+      $emoji.click()
+      expect($block.find(selector).length).toBe 0
diff --git a/spec/javascripts/behaviors/quick_submit_spec.js.coffee b/spec/javascripts/behaviors/quick_submit_spec.js.coffee
index 09708c12ed43b535c4f1d91dfb212a4461cdc93b..d3b003a328a70213b172255b22e38501100237ba 100644
--- a/spec/javascripts/behaviors/quick_submit_spec.js.coffee
+++ b/spec/javascripts/behaviors/quick_submit_spec.js.coffee
@@ -14,17 +14,17 @@ describe 'Quick Submit behavior', ->
     }
 
   it 'does not respond to other keyCodes', ->
-    $('input').trigger(keydownEvent(keyCode: 32))
+    $('input.quick-submit-input').trigger(keydownEvent(keyCode: 32))
 
     expect(@spies.submit).not.toHaveBeenTriggered()
 
   it 'does not respond to Enter alone', ->
-    $('input').trigger(keydownEvent(ctrlKey: false, metaKey: false))
+    $('input.quick-submit-input').trigger(keydownEvent(ctrlKey: false, metaKey: false))
 
     expect(@spies.submit).not.toHaveBeenTriggered()
 
   it 'does not respond to repeated events', ->
-    $('input').trigger(keydownEvent(repeat: true))
+    $('input.quick-submit-input').trigger(keydownEvent(repeat: true))
 
     expect(@spies.submit).not.toHaveBeenTriggered()
 
@@ -38,26 +38,26 @@ describe 'Quick Submit behavior', ->
   # only run the tests that apply to the current platform
   if navigator.userAgent.match(/Macintosh/)
     it 'responds to Meta+Enter', ->
-      $('input').trigger(keydownEvent())
+      $('input.quick-submit-input').trigger(keydownEvent())
 
       expect(@spies.submit).toHaveBeenTriggered()
 
     it 'excludes other modifier keys', ->
-      $('input').trigger(keydownEvent(altKey: true))
-      $('input').trigger(keydownEvent(ctrlKey: true))
-      $('input').trigger(keydownEvent(shiftKey: true))
+      $('input.quick-submit-input').trigger(keydownEvent(altKey: true))
+      $('input.quick-submit-input').trigger(keydownEvent(ctrlKey: true))
+      $('input.quick-submit-input').trigger(keydownEvent(shiftKey: true))
 
       expect(@spies.submit).not.toHaveBeenTriggered()
   else
     it 'responds to Ctrl+Enter', ->
-      $('input').trigger(keydownEvent())
+      $('input.quick-submit-input').trigger(keydownEvent())
 
       expect(@spies.submit).toHaveBeenTriggered()
 
     it 'excludes other modifier keys', ->
-      $('input').trigger(keydownEvent(altKey: true))
-      $('input').trigger(keydownEvent(metaKey: true))
-      $('input').trigger(keydownEvent(shiftKey: true))
+      $('input.quick-submit-input').trigger(keydownEvent(altKey: true))
+      $('input.quick-submit-input').trigger(keydownEvent(metaKey: true))
+      $('input.quick-submit-input').trigger(keydownEvent(shiftKey: true))
 
       expect(@spies.submit).not.toHaveBeenTriggered()
 
diff --git a/spec/javascripts/fixtures/application.html.haml b/spec/javascripts/fixtures/application.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..3fc6114407d6197bf74c959228d4d0ca96a55e9c
--- /dev/null
+++ b/spec/javascripts/fixtures/application.html.haml
@@ -0,0 +1,2 @@
+%a#test-link.btn.disabled{:href => "/foo"} Test link
+%button#test-button.btn.disabled Test Button
diff --git a/spec/javascripts/fixtures/awards_handler.html.haml b/spec/javascripts/fixtures/awards_handler.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..d55936ee4f9e61f737c735b8e4212a92288e519e
--- /dev/null
+++ b/spec/javascripts/fixtures/awards_handler.html.haml
@@ -0,0 +1,52 @@
+.issue-details.issuable-details
+  .detail-page-description.content-block
+    %h2.title Quibusdam sint officiis earum molestiae ipsa autem voluptatem nisi rem.
+    .description.js-task-list-container.is-task-list-enabled
+      .wiki
+        %p Qui exercitationem magnam optio quae fuga earum odio.
+        %textarea.hidden.js-task-list-field Qui exercitationem magnam optio quae fuga earum odio.
+      %small.edited-text
+    .content-block.content-block-small
+      .awards.js-awards-block{"data-award-url" => "/gitlab-org/gitlab-test/issues/8/toggle_award_emoji"}
+        %button.award-control.btn.js-emoji-btn{"data-placement" => "bottom", "data-title" => "", :type => "button"}
+          .icon.emoji-icon.emoji-1F44D{"data-aliases" => "", "data-emoji" => "thumbsup", "data-unicode-name" => "1F44D", :title => "thumbsup"}
+          %span.award-control-text.js-counter 0
+        %button.award-control.btn.js-emoji-btn{"data-placement" => "bottom", "data-title" => "", :type => "button"}
+          .icon.emoji-icon.emoji-1F44E{"data-aliases" => "", "data-emoji" => "thumbsdown", "data-unicode-name" => "1F44E", :title => "thumbsdown"}
+          %span.award-control-text.js-counter 0
+        .award-menu-holder.js-award-holder
+          %button.btn.award-control.js-add-award{:type => "button"}
+            %i.fa.fa-smile-o.award-control-icon.award-control-icon-normal
+            %i.fa.fa-spinner.fa-spin.award-control-icon.award-control-icon-loading
+            %span.award-control-text Add
+    %section.issuable-discussion
+      #notes
+        %ul#notes-list.notes.main-notes-list.timeline
+          %li#note_348.note.note-row-348.timeline-entry{"data-author-id" => "18", "data-editable" => ""}
+            .timeline-entry-inner
+              .timeline-icon
+                %a{:href => "/u/agustin"}
+                  %img.avatar.s40{:alt => "", :src => "#"}/
+              .timeline-content
+                .note-header
+                  %a.author_link{:href => "/u/agustin"}
+                    %span.author Brenna Stokes
+                  .inline.note-headline-light
+                    @agustin commented
+                    %a{:href => "#note_348"}
+                      %time 11 days ago
+                  .note-actions
+                    %span.note-role Reporter
+                    %a.note-action-button.note-emoji-button.js-add-award.js-note-emoji{"data-position" => "right", :href => "#", :title => "Award Emoji"}
+                      %i.fa.fa-spinner.fa-spin
+                      %i.fa.fa-smile-o
+                .js-task-list-container.note-body.is-task-list-enabled
+                  .note-text
+                    %p Suscipit sunt quia quisquam sed eveniet ipsam.
+                  .note-awards
+                    .awards.hidden.js-awards-block{"data-award-url" => "/gitlab-org/gitlab-test/notes/348/toggle_award_emoji"}
+                      .award-menu-holder.js-award-holder
+                        %button.btn.award-control.js-add-award{:type => "button"}
+                          %i.fa.fa-smile-o.award-control-icon.award-control-icon-normal
+                          %i.fa.fa-spinner.fa-spin.award-control-icon.award-control-icon-loading
+                          %span.award-control-text Add
diff --git a/spec/javascripts/fixtures/behaviors/quick_submit.html.haml b/spec/javascripts/fixtures/behaviors/quick_submit.html.haml
index e3788bee8131e2aaf20b3121a86a495469a3f3f5..dc2ceed42f4a22d067c59ed515e7dc595a79dd57 100644
--- a/spec/javascripts/fixtures/behaviors/quick_submit.html.haml
+++ b/spec/javascripts/fixtures/behaviors/quick_submit.html.haml
@@ -1,5 +1,5 @@
 %form.js-quick-submit{ action: '/foo' }
-  %input{ type: 'text' }
+  %input{ type: 'text', class: 'quick-submit-input'}
   %textarea
 
   %input{ type: 'submit'} Submit
diff --git a/spec/javascripts/fixtures/emoji_menu.coffee b/spec/javascripts/fixtures/emoji_menu.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..e529dd5f1cd2d6b9df8ca4cc360c9dd214991cf4
--- /dev/null
+++ b/spec/javascripts/fixtures/emoji_menu.coffee
@@ -0,0 +1,957 @@
+window.emojiMenu = """
+  <div class='emoji-menu'>
+    <div class='emoji-menu-content'>
+      <input type="text" name="emoji_search" id="emoji_search" value="" class="emoji-search search-input form-control" />
+      <h5 class='emoji-menu-title'>
+      Emoticons
+      </h5>
+      <ul class='clearfix emoji-menu-list'>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F47D" title="alien" data-aliases="" data-emoji="alien" data-unicode-name="1F47D"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F47C" title="angel" data-aliases="" data-emoji="angel" data-unicode-name="1F47C"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F4A2" title="anger" data-aliases="" data-emoji="anger" data-unicode-name="1F4A2"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F620" title="angry" data-aliases="" data-emoji="angry" data-unicode-name="1F620"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F627" title="anguished" data-aliases="" data-emoji="anguished" data-unicode-name="1F627"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F632" title="astonished" data-aliases="" data-emoji="astonished" data-unicode-name="1F632"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F45F" title="athletic_shoe" data-aliases="" data-emoji="athletic_shoe" data-unicode-name="1F45F"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F476" title="baby" data-aliases="" data-emoji="baby" data-unicode-name="1F476"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F459" title="bikini" data-aliases="" data-emoji="bikini" data-unicode-name="1F459"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F499" title="blue_heart" data-aliases="" data-emoji="blue_heart" data-unicode-name="1F499"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F60A" title="blush" data-aliases="" data-emoji="blush" data-unicode-name="1F60A"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F4A5" title="boom" data-aliases="" data-emoji="boom" data-unicode-name="1F4A5"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F462" title="boot" data-aliases="" data-emoji="boot" data-unicode-name="1F462"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F647" title="bow" data-aliases="" data-emoji="bow" data-unicode-name="1F647"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F466" title="boy" data-aliases="" data-emoji="boy" data-unicode-name="1F466"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F470" title="bride_with_veil" data-aliases="" data-emoji="bride_with_veil" data-unicode-name="1F470"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F4BC" title="briefcase" data-aliases="" data-emoji="briefcase" data-unicode-name="1F4BC"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F494" title="broken_heart" data-aliases="" data-emoji="broken_heart" data-unicode-name="1F494"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F464" title="bust_in_silhouette" data-aliases="" data-emoji="bust_in_silhouette" data-unicode-name="1F464"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F465" title="busts_in_silhouette" data-aliases="" data-emoji="busts_in_silhouette" data-unicode-name="1F465"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F44F" title="clap" data-aliases="" data-emoji="clap" data-unicode-name="1F44F"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F302" title="closed_umbrella" data-aliases="" data-emoji="closed_umbrella" data-unicode-name="1F302"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F630" title="cold_sweat" data-aliases="" data-emoji="cold_sweat" data-unicode-name="1F630"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F616" title="confounded" data-aliases="" data-emoji="confounded" data-unicode-name="1F616"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F615" title="confused" data-aliases="" data-emoji="confused" data-unicode-name="1F615"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F477" title="construction_worker" data-aliases="" data-emoji="construction_worker" data-unicode-name="1F477"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F46E" title="cop" data-aliases="" data-emoji="cop" data-unicode-name="1F46E"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F46B" title="couple" data-aliases="" data-emoji="couple" data-unicode-name="1F46B"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F491" title="couple_with_heart" data-aliases="" data-emoji="couple_with_heart" data-unicode-name="1F491"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F48F" title="couplekiss" data-aliases="" data-emoji="couplekiss" data-unicode-name="1F48F"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F451" title="crown" data-aliases="" data-emoji="crown" data-unicode-name="1F451"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F622" title="cry" data-aliases="" data-emoji="cry" data-unicode-name="1F622"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F63F" title="crying_cat_face" data-aliases="" data-emoji="crying_cat_face" data-unicode-name="1F63F"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F498" title="cupid" data-aliases="" data-emoji="cupid" data-unicode-name="1F498"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F483" title="dancer" data-aliases="" data-emoji="dancer" data-unicode-name="1F483"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F46F" title="dancers" data-aliases="" data-emoji="dancers" data-unicode-name="1F46F"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F4A8" title="dash" data-aliases="" data-emoji="dash" data-unicode-name="1F4A8"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F61E" title="disappointed" data-aliases="" data-emoji="disappointed" data-unicode-name="1F61E"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F625" title="disappointed_relieved" data-aliases="" data-emoji="disappointed_relieved" data-unicode-name="1F625"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F4AB" title="dizzy" data-aliases="" data-emoji="dizzy" data-unicode-name="1F4AB"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F635" title="dizzy_face" data-aliases="" data-emoji="dizzy_face" data-unicode-name="1F635"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F457" title="dress" data-aliases="" data-emoji="dress" data-unicode-name="1F457"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F4A7" title="droplet" data-aliases="" data-emoji="droplet" data-unicode-name="1F4A7"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F442" title="ear" data-aliases="" data-emoji="ear" data-unicode-name="1F442"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F611" title="expressionless" data-aliases="" data-emoji="expressionless" data-unicode-name="1F611"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F453" title="eyeglasses" data-aliases="" data-emoji="eyeglasses" data-unicode-name="1F453"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F440" title="eyes" data-aliases="" data-emoji="eyes" data-unicode-name="1F440"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F46A" title="family" data-aliases="" data-emoji="family" data-unicode-name="1F46A"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F628" title="fearful" data-aliases="" data-emoji="fearful" data-unicode-name="1F628"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F525" title="fire" data-aliases=":flame:" data-emoji="fire" data-unicode-name="1F525"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-270A" title="fist" data-aliases="" data-emoji="fist" data-unicode-name="270A"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F633" title="flushed" data-aliases="" data-emoji="flushed" data-unicode-name="1F633"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F463" title="footprints" data-aliases="" data-emoji="footprints" data-unicode-name="1F463"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F626" title="frowning" data-aliases=":anguished:" data-emoji="frowning" data-unicode-name="1F626"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F48E" title="gem" data-aliases="" data-emoji="gem" data-unicode-name="1F48E"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F467" title="girl" data-aliases="" data-emoji="girl" data-unicode-name="1F467"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F49A" title="green_heart" data-aliases="" data-emoji="green_heart" data-unicode-name="1F49A"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F62C" title="grimacing" data-aliases="" data-emoji="grimacing" data-unicode-name="1F62C"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F601" title="grin" data-aliases="" data-emoji="grin" data-unicode-name="1F601"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F600" title="grinning" data-aliases="" data-emoji="grinning" data-unicode-name="1F600"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F482" title="guardsman" data-aliases="" data-emoji="guardsman" data-unicode-name="1F482"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F487" title="haircut" data-aliases="" data-emoji="haircut" data-unicode-name="1F487"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F45C" title="handbag" data-aliases="" data-emoji="handbag" data-unicode-name="1F45C"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F649" title="hear_no_evil" data-aliases="" data-emoji="hear_no_evil" data-unicode-name="1F649"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-2764" title="heart" data-aliases="" data-emoji="heart" data-unicode-name="2764"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F60D" title="heart_eyes" data-aliases="" data-emoji="heart_eyes" data-unicode-name="1F60D"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F63B" title="heart_eyes_cat" data-aliases="" data-emoji="heart_eyes_cat" data-unicode-name="1F63B"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F493" title="heartbeat" data-aliases="" data-emoji="heartbeat" data-unicode-name="1F493"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F497" title="heartpulse" data-aliases="" data-emoji="heartpulse" data-unicode-name="1F497"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F460" title="high_heel" data-aliases="" data-emoji="high_heel" data-unicode-name="1F460"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F62F" title="hushed" data-aliases="" data-emoji="hushed" data-unicode-name="1F62F"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F47F" title="imp" data-aliases="" data-emoji="imp" data-unicode-name="1F47F"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F481" title="information_desk_person" data-aliases="" data-emoji="information_desk_person" data-unicode-name="1F481"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F607" title="innocent" data-aliases="" data-emoji="innocent" data-unicode-name="1F607"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F47A" title="japanese_goblin" data-aliases="" data-emoji="japanese_goblin" data-unicode-name="1F47A"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F479" title="japanese_ogre" data-aliases="" data-emoji="japanese_ogre" data-unicode-name="1F479"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F456" title="jeans" data-aliases="" data-emoji="jeans" data-unicode-name="1F456"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F602" title="joy" data-aliases="" data-emoji="joy" data-unicode-name="1F602"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F639" title="joy_cat" data-aliases="" data-emoji="joy_cat" data-unicode-name="1F639"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F458" title="kimono" data-aliases="" data-emoji="kimono" data-unicode-name="1F458"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F48B" title="kiss" data-aliases="" data-emoji="kiss" data-unicode-name="1F48B"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F617" title="kissing" data-aliases="" data-emoji="kissing" data-unicode-name="1F617"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F63D" title="kissing_cat" data-aliases="" data-emoji="kissing_cat" data-unicode-name="1F63D"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F61A" title="kissing_closed_eyes" data-aliases="" data-emoji="kissing_closed_eyes" data-unicode-name="1F61A"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F618" title="kissing_heart" data-aliases="" data-emoji="kissing_heart" data-unicode-name="1F618"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F619" title="kissing_smiling_eyes" data-aliases="" data-emoji="kissing_smiling_eyes" data-unicode-name="1F619"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F606" title="laughing" data-aliases=":satisfied:" data-emoji="laughing" data-unicode-name="1F606"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F444" title="lips" data-aliases="" data-emoji="lips" data-unicode-name="1F444"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F484" title="lipstick" data-aliases="" data-emoji="lipstick" data-unicode-name="1F484"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F48C" title="love_letter" data-aliases="" data-emoji="love_letter" data-unicode-name="1F48C"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F468" title="man" data-aliases="" data-emoji="man" data-unicode-name="1F468"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F472" title="man_with_gua_pi_mao" data-aliases="" data-emoji="man_with_gua_pi_mao" data-unicode-name="1F472"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F473" title="man_with_turban" data-aliases="" data-emoji="man_with_turban" data-unicode-name="1F473"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F45E" title="mans_shoe" data-aliases="" data-emoji="mans_shoe" data-unicode-name="1F45E"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F637" title="mask" data-aliases="" data-emoji="mask" data-unicode-name="1F637"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F486" title="massage" data-aliases="" data-emoji="massage" data-unicode-name="1F486"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F4AA" title="muscle" data-aliases="" data-emoji="muscle" data-unicode-name="1F4AA"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F485" title="nail_care" data-aliases="" data-emoji="nail_care" data-unicode-name="1F485"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F454" title="necktie" data-aliases="" data-emoji="necktie" data-unicode-name="1F454"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F610" title="neutral_face" data-aliases="" data-emoji="neutral_face" data-unicode-name="1F610"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F645" title="no_good" data-aliases="" data-emoji="no_good" data-unicode-name="1F645"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F636" title="no_mouth" data-aliases="" data-emoji="no_mouth" data-unicode-name="1F636"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F443" title="nose" data-aliases="" data-emoji="nose" data-unicode-name="1F443"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F44C" title="ok_hand" data-aliases="" data-emoji="ok_hand" data-unicode-name="1F44C"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F646" title="ok_woman" data-aliases="" data-emoji="ok_woman" data-unicode-name="1F646"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F474" title="older_man" data-aliases="" data-emoji="older_man" data-unicode-name="1F474"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F475" title="older_woman" data-aliases=":grandma:" data-emoji="older_woman" data-unicode-name="1F475"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F450" title="open_hands" data-aliases="" data-emoji="open_hands" data-unicode-name="1F450"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F62E" title="open_mouth" data-aliases="" data-emoji="open_mouth" data-unicode-name="1F62E"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F614" title="pensive" data-aliases="" data-emoji="pensive" data-unicode-name="1F614"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F623" title="persevere" data-aliases="" data-emoji="persevere" data-unicode-name="1F623"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F64D" title="person_frowning" data-aliases="" data-emoji="person_frowning" data-unicode-name="1F64D"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F471" title="person_with_blond_hair" data-aliases="" data-emoji="person_with_blond_hair" data-unicode-name="1F471"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F64E" title="person_with_pouting_face" data-aliases="" data-emoji="person_with_pouting_face" data-unicode-name="1F64E"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F447" title="point_down" data-aliases="" data-emoji="point_down" data-unicode-name="1F447"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F448" title="point_left" data-aliases="" data-emoji="point_left" data-unicode-name="1F448"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F449" title="point_right" data-aliases="" data-emoji="point_right" data-unicode-name="1F449"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-261D" title="point_up" data-aliases="" data-emoji="point_up" data-unicode-name="261D"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F446" title="point_up_2" data-aliases="" data-emoji="point_up_2" data-unicode-name="1F446"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F4A9" title="poop" data-aliases=":shit: :hankey: :poo:" data-emoji="poop" data-unicode-name="1F4A9"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F45D" title="pouch" data-aliases="" data-emoji="pouch" data-unicode-name="1F45D"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F63E" title="pouting_cat" data-aliases="" data-emoji="pouting_cat" data-unicode-name="1F63E"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F64F" title="pray" data-aliases="" data-emoji="pray" data-unicode-name="1F64F"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F478" title="princess" data-aliases="" data-emoji="princess" data-unicode-name="1F478"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F44A" title="punch" data-aliases="" data-emoji="punch" data-unicode-name="1F44A"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F49C" title="purple_heart" data-aliases="" data-emoji="purple_heart" data-unicode-name="1F49C"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F45B" title="purse" data-aliases="" data-emoji="purse" data-unicode-name="1F45B"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F621" title="rage" data-aliases="" data-emoji="rage" data-unicode-name="1F621"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-270B" title="raised_hand" data-aliases="" data-emoji="raised_hand" data-unicode-name="270B"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F64C" title="raised_hands" data-aliases="" data-emoji="raised_hands" data-unicode-name="1F64C"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F64B" title="raising_hand" data-aliases="" data-emoji="raising_hand" data-unicode-name="1F64B"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-263A" title="relaxed" data-aliases="" data-emoji="relaxed" data-unicode-name="263A"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F60C" title="relieved" data-aliases="" data-emoji="relieved" data-unicode-name="1F60C"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F49E" title="revolving_hearts" data-aliases="" data-emoji="revolving_hearts" data-unicode-name="1F49E"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F380" title="ribbon" data-aliases="" data-emoji="ribbon" data-unicode-name="1F380"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F48D" title="ring" data-aliases="" data-emoji="ring" data-unicode-name="1F48D"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F3C3" title="runner" data-aliases="" data-emoji="runner" data-unicode-name="1F3C3"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F3BD" title="running_shirt_with_sash" data-aliases="" data-emoji="running_shirt_with_sash" data-unicode-name="1F3BD"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F461" title="sandal" data-aliases="" data-emoji="sandal" data-unicode-name="1F461"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F631" title="scream" data-aliases="" data-emoji="scream" data-unicode-name="1F631"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F640" title="scream_cat" data-aliases="" data-emoji="scream_cat" data-unicode-name="1F640"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F648" title="see_no_evil" data-aliases="" data-emoji="see_no_evil" data-unicode-name="1F648"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F455" title="shirt" data-aliases="" data-emoji="shirt" data-unicode-name="1F455"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F480" title="skull" data-aliases=":skeleton:" data-emoji="skull" data-unicode-name="1F480"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F634" title="sleeping" data-aliases="" data-emoji="sleeping" data-unicode-name="1F634"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F62A" title="sleepy" data-aliases="" data-emoji="sleepy" data-unicode-name="1F62A"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F604" title="smile" data-aliases="" data-emoji="smile" data-unicode-name="1F604"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F638" title="smile_cat" data-aliases="" data-emoji="smile_cat" data-unicode-name="1F638"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F603" title="smiley" data-aliases="" data-emoji="smiley" data-unicode-name="1F603"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F63A" title="smiley_cat" data-aliases="" data-emoji="smiley_cat" data-unicode-name="1F63A"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F608" title="smiling_imp" data-aliases="" data-emoji="smiling_imp" data-unicode-name="1F608"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F60F" title="smirk" data-aliases="" data-emoji="smirk" data-unicode-name="1F60F"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F63C" title="smirk_cat" data-aliases="" data-emoji="smirk_cat" data-unicode-name="1F63C"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F62D" title="sob" data-aliases="" data-emoji="sob" data-unicode-name="1F62D"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-2728" title="sparkles" data-aliases="" data-emoji="sparkles" data-unicode-name="2728"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F496" title="sparkling_heart" data-aliases="" data-emoji="sparkling_heart" data-unicode-name="1F496"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F64A" title="speak_no_evil" data-aliases="" data-emoji="speak_no_evil" data-unicode-name="1F64A"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F4AC" title="speech_balloon" data-aliases="" data-emoji="speech_balloon" data-unicode-name="1F4AC"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F31F" title="star2" data-aliases="" data-emoji="star2" data-unicode-name="1F31F"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F61B" title="stuck_out_tongue" data-aliases="" data-emoji="stuck_out_tongue" data-unicode-name="1F61B"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F61D" title="stuck_out_tongue_closed_eyes" data-aliases="" data-emoji="stuck_out_tongue_closed_eyes" data-unicode-name="1F61D"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F61C" title="stuck_out_tongue_winking_eye" data-aliases="" data-emoji="stuck_out_tongue_winking_eye" data-unicode-name="1F61C"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F60E" title="sunglasses" data-aliases="" data-emoji="sunglasses" data-unicode-name="1F60E"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F613" title="sweat" data-aliases="" data-emoji="sweat" data-unicode-name="1F613"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F4A6" title="sweat_drops" data-aliases="" data-emoji="sweat_drops" data-unicode-name="1F4A6"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F605" title="sweat_smile" data-aliases="" data-emoji="sweat_smile" data-unicode-name="1F605"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F4AD" title="thought_balloon" data-aliases="" data-emoji="thought_balloon" data-unicode-name="1F4AD"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F44E" title="thumbsdown" data-aliases=":-1:" data-emoji="thumbsdown" data-unicode-name="1F44E"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F44D" title="thumbsup" data-aliases=":+1:" data-emoji="thumbsup" data-unicode-name="1F44D"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F62B" title="tired_face" data-aliases="" data-emoji="tired_face" data-unicode-name="1F62B"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F445" title="tongue" data-aliases="" data-emoji="tongue" data-unicode-name="1F445"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F3A9" title="tophat" data-aliases="" data-emoji="tophat" data-unicode-name="1F3A9"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F624" title="triumph" data-aliases="" data-emoji="triumph" data-unicode-name="1F624"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F495" title="two_hearts" data-aliases="" data-emoji="two_hearts" data-unicode-name="1F495"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F46C" title="two_men_holding_hands" data-aliases="" data-emoji="two_men_holding_hands" data-unicode-name="1F46C"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F46D" title="two_women_holding_hands" data-aliases="" data-emoji="two_women_holding_hands" data-unicode-name="1F46D"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F612" title="unamused" data-aliases="" data-emoji="unamused" data-unicode-name="1F612"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-270C" title="v" data-aliases="" data-emoji="v" data-unicode-name="270C"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F6B6" title="walking" data-aliases="" data-emoji="walking" data-unicode-name="1F6B6"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F44B" title="wave" data-aliases="" data-emoji="wave" data-unicode-name="1F44B"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F629" title="weary" data-aliases="" data-emoji="weary" data-unicode-name="1F629"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F609" title="wink" data-aliases="" data-emoji="wink" data-unicode-name="1F609"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F469" title="woman" data-aliases="" data-emoji="woman" data-unicode-name="1F469"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F45A" title="womans_clothes" data-aliases="" data-emoji="womans_clothes" data-unicode-name="1F45A"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F452" title="womans_hat" data-aliases="" data-emoji="womans_hat" data-unicode-name="1F452"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F61F" title="worried" data-aliases="" data-emoji="worried" data-unicode-name="1F61F"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F49B" title="yellow_heart" data-aliases="" data-emoji="yellow_heart" data-unicode-name="1F49B"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F60B" title="yum" data-aliases="" data-emoji="yum" data-unicode-name="1F60B"></div>
+          </button>
+        </li>
+        <li class='pull-left text-center emoji-menu-list-item'>
+          <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>
+          <div class="icon emoji-icon emoji-1F4A4" title="zzz" data-aliases="" data-emoji="zzz" data-unicode-name="1F4A4"></div>
+          </button>
+        </li>
+      </ul>
+    </div>
+  </div>
+"""
diff --git a/spec/javascripts/fixtures/right_sidebar.html.haml b/spec/javascripts/fixtures/right_sidebar.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..95efaff4b694cb4419db8b227e7955d3bac22da8
--- /dev/null
+++ b/spec/javascripts/fixtures/right_sidebar.html.haml
@@ -0,0 +1,13 @@
+%div
+  %div.page-gutter.page-with-sidebar
+
+  %aside.right-sidebar
+    %div.block.issuable-sidebar-header
+      %a.gutter-toggle.pull-right.js-sidebar-toggle
+        %i.fa.fa-angle-double-left
+
+    %form.issuable-context-form
+      %div.block.labels
+        %div.sidebar-collapsed-icon
+          %i.fa.fa-tags
+          %span 1
diff --git a/spec/javascripts/fixtures/search_autocomplete.html.haml b/spec/javascripts/fixtures/search_autocomplete.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..7785120da5bc6a02f69477f25f079fe2b8f2dd63
--- /dev/null
+++ b/spec/javascripts/fixtures/search_autocomplete.html.haml
@@ -0,0 +1,10 @@
+.search.search-form.has-location-badge
+  %form.navbar-form
+    .search-input-container
+      %div.location-badge
+        This project
+      .search-input-wrap
+        .dropdown
+          %input#search.search-input.dropdown-menu-toggle
+          .dropdown-menu.dropdown-select
+            .dropdown-content
diff --git a/spec/javascripts/fixtures/u2f/authenticate.html.haml b/spec/javascripts/fixtures/u2f/authenticate.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..859e79a6c9ede2cfd1b3090588b987ce8872b18d
--- /dev/null
+++ b/spec/javascripts/fixtures/u2f/authenticate.html.haml
@@ -0,0 +1 @@
+= render partial: "u2f/authenticate", locals: { new_user_session_path: "/users/sign_in" }
diff --git a/spec/javascripts/fixtures/u2f/register.html.haml b/spec/javascripts/fixtures/u2f/register.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..5ed51be689cda34401d273ae599185eb90f0bbde
--- /dev/null
+++ b/spec/javascripts/fixtures/u2f/register.html.haml
@@ -0,0 +1,2 @@
+- user = FactoryGirl.build(:user, :two_factor_via_otp)
+= render partial: "u2f/register", locals: { create_u2f_profile_two_factor_auth_path: '/profile/two_factor_auth/create_u2f', current_user: user }
diff --git a/spec/javascripts/stat_graph_contributors_graph_spec.js b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
similarity index 98%
rename from spec/javascripts/stat_graph_contributors_graph_spec.js
rename to spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
index 78d39f1b4280d6480ccff83cf6e2e9961897f84a..82ee1954a597e44a337c68e50ae398b08acf889b 100644
--- a/spec/javascripts/stat_graph_contributors_graph_spec.js
+++ b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
@@ -1,4 +1,4 @@
-//= require stat_graph_contributors_graph
+//= require graphs/stat_graph_contributors_graph
 
 describe("ContributorsGraph", function () {
   describe("#set_x_domain", function () {
diff --git a/spec/javascripts/stat_graph_contributors_util_spec.js b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
similarity index 98%
rename from spec/javascripts/stat_graph_contributors_util_spec.js
rename to spec/javascripts/graphs/stat_graph_contributors_util_spec.js
index dbafe782b773326d03790d50063067f143e091dd..56970e22e347810df05e6e1101e70b6d3b8b604c 100644
--- a/spec/javascripts/stat_graph_contributors_util_spec.js
+++ b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
@@ -1,4 +1,4 @@
-//= require stat_graph_contributors_util
+//= require graphs/stat_graph_contributors_util
 
 describe("ContributorsStatGraphUtil", function () {
 
@@ -9,14 +9,14 @@ describe("ContributorsStatGraphUtil", function () {
             {author_email: "dzaporozhets@email.com", author_name: "Dmitriy Zaporozhets", date: "2013-05-08", additions: 6, deletions: 1},
             {author_email: "dzaporozhets@email.com", author_name: "Dmitriy Zaporozhets", date: "2013-05-08", additions: 19, deletions: 3},
             {author_email: "dzaporozhets@email.com", author_name: "Dmitriy Zaporozhets", date: "2013-05-08", additions: 29, deletions: 3}]
-      
+
       var correct_parsed_log = {
         total: [
         {date: "2013-05-09", additions: 471, deletions: 0, commits: 1},
         {date: "2013-05-08", additions: 54, deletions: 7, commits: 3}],
         by_author:
         [
-        { 
+        {
           author_name: "Karlo Soriano", author_email: "karlo@email.com",
           "2013-05-09": {date: "2013-05-09", additions: 471, deletions: 0, commits: 1}
         },
@@ -132,8 +132,8 @@ describe("ContributorsStatGraphUtil", function () {
       total: [{date: "2013-05-09", additions: 471, deletions: 0, commits: 1},
       {date: "2013-05-08", additions: 54, deletions: 7, commits: 3}],
       by_author:[
-      { 
-        author: "Karlo Soriano", 
+      {
+        author: "Karlo Soriano",
         "2013-05-09": {date: "2013-05-09", additions: 471, deletions: 0, commits: 1}
       },
       {
@@ -161,11 +161,11 @@ describe("ContributorsStatGraphUtil", function () {
     it("returns the log by author sorted by specified field", function () {
       var fake_parsed_log = {
         total: [
-          {date: "2013-05-09", additions: 471, deletions: 0, commits: 1}, 
+          {date: "2013-05-09", additions: 471, deletions: 0, commits: 1},
           {date: "2013-05-08", additions: 54, deletions: 7, commits: 3}
         ],
         by_author: [
-          { 
+          {
             author_name: "Karlo Soriano", author_email: "karlo@email.com",
             "2013-05-09": {date: "2013-05-09", additions: 471, deletions: 0, commits: 1}
           },
diff --git a/spec/javascripts/stat_graph_spec.js b/spec/javascripts/graphs/stat_graph_spec.js
similarity index 92%
rename from spec/javascripts/stat_graph_spec.js
rename to spec/javascripts/graphs/stat_graph_spec.js
index 4c652910cd6f43a5722c89f1e3bfd21076284fa5..4b05d401a428fbd328d7d9fcd7ee091d2227f383 100644
--- a/spec/javascripts/stat_graph_spec.js
+++ b/spec/javascripts/graphs/stat_graph_spec.js
@@ -1,4 +1,4 @@
-//= require stat_graph
+//= require graphs/stat_graph
 
 describe("StatGraph", function () {
 
diff --git a/spec/javascripts/new_branch_spec.js.coffee b/spec/javascripts/new_branch_spec.js.coffee
index f2ce85efcdcbbaac28c81ec75867d3723f587833..ce7737938174c3b3ffb3ed6fb60550ad0759e444 100644
--- a/spec/javascripts/new_branch_spec.js.coffee
+++ b/spec/javascripts/new_branch_spec.js.coffee
@@ -1,4 +1,4 @@
-#= require jquery-ui
+#= require jquery-ui/autocomplete
 #= require new_branch_form
 
 describe 'Branch', ->
diff --git a/spec/javascripts/notes_spec.js.coffee b/spec/javascripts/notes_spec.js.coffee
index dd160e821b3d6e54610ac184e87f7d701e6350ec..3a3c8d63e82f7106b25bc4c3687aa02f30561b11 100644
--- a/spec/javascripts/notes_spec.js.coffee
+++ b/spec/javascripts/notes_spec.js.coffee
@@ -1,7 +1,7 @@
 #= require notes
 #= require gl_form
 
-window.gon = {}
+window.gon or= {}
 window.disableButtonIfEmptyField = -> null
 
 describe 'Notes', ->
diff --git a/spec/javascripts/project_title_spec.js.coffee b/spec/javascripts/project_title_spec.js.coffee
index 3d8de2ff989bde00db9d7964192a35f0188289be..9be29097f4c6ee3af480d2b54804ada3710d30c1 100644
--- a/spec/javascripts/project_title_spec.js.coffee
+++ b/spec/javascripts/project_title_spec.js.coffee
@@ -1,11 +1,12 @@
 #= require bootstrap
 #= require select2
+#= require lib/type_utility
 #= require gl_dropdown
 #= require api
 #= require project_select
 #= require project
 
-window.gon = {}
+window.gon or= {}
 window.gon.api_version = 'v3'
 
 describe 'Project Title', ->
diff --git a/spec/javascripts/right_sidebar_spec.js.coffee b/spec/javascripts/right_sidebar_spec.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..2075cacdb674148299f8c8ef911358b610840d02
--- /dev/null
+++ b/spec/javascripts/right_sidebar_spec.js.coffee
@@ -0,0 +1,69 @@
+#= require right_sidebar
+#= require jquery
+#= require jquery.cookie
+
+@sidebar    = null
+$aside      = null
+$toggle     = null
+$icon       = null
+$page       = null
+$labelsIcon = null
+
+
+assertSidebarState = (state) ->
+
+  shouldBeExpanded  = state is 'expanded'
+  shouldBeCollapsed = state is 'collapsed'
+
+  expect($aside.hasClass('right-sidebar-expanded')).toBe shouldBeExpanded
+  expect($page.hasClass('right-sidebar-expanded')).toBe shouldBeExpanded
+  expect($icon.hasClass('fa-angle-double-right')).toBe shouldBeExpanded
+
+  expect($aside.hasClass('right-sidebar-collapsed')).toBe shouldBeCollapsed
+  expect($page.hasClass('right-sidebar-collapsed')).toBe shouldBeCollapsed
+  expect($icon.hasClass('fa-angle-double-left')).toBe shouldBeCollapsed
+
+
+describe 'RightSidebar', ->
+
+  fixture.preload 'right_sidebar.html'
+
+  beforeEach ->
+    fixture.load 'right_sidebar.html'
+
+    @sidebar    = new Sidebar
+    $aside      = $ '.right-sidebar'
+    $page       = $ '.page-with-sidebar'
+    $icon       = $aside.find 'i'
+    $toggle     = $aside.find '.js-sidebar-toggle'
+    $labelsIcon = $aside.find '.sidebar-collapsed-icon'
+
+
+  it 'should expand the sidebar when arrow is clicked', ->
+
+    $toggle.click()
+    assertSidebarState 'expanded'
+
+
+  it 'should collapse the sidebar when arrow is clicked', ->
+
+    $toggle.click()
+    assertSidebarState 'expanded'
+
+    $toggle.click()
+    assertSidebarState 'collapsed'
+
+
+  it 'should float over the page and when sidebar icons clicked', ->
+
+    $labelsIcon.click()
+    assertSidebarState 'expanded'
+
+
+  it 'should collapse when the icon arrow clicked while it is floating on page', ->
+
+    $labelsIcon.click()
+    assertSidebarState 'expanded'
+
+    $toggle.click()
+    assertSidebarState 'collapsed'
diff --git a/spec/javascripts/search_autocomplete_spec.js.coffee b/spec/javascripts/search_autocomplete_spec.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..e77177783a7656019d2902e2dab2d0c4163ba3a9
--- /dev/null
+++ b/spec/javascripts/search_autocomplete_spec.js.coffee
@@ -0,0 +1,149 @@
+#= require gl_dropdown
+#= require search_autocomplete
+#= require jquery
+#= require lib/common_utils
+#= require lib/type_utility
+#= require fuzzaldrin-plus
+
+
+widget       = null
+userId       = 1
+window.gon or= {}
+window.gon.current_user_id = userId
+
+dashboardIssuesPath = '/dashboard/issues'
+dashboardMRsPath    = '/dashboard/merge_requests'
+projectIssuesPath   = '/gitlab-org/gitlab-ce/issues'
+projectMRsPath      = '/gitlab-org/gitlab-ce/merge_requests'
+groupIssuesPath     = '/groups/gitlab-org/issues'
+groupMRsPath        = '/groups/gitlab-org/merge_requests'
+projectName         = 'GitLab Community Edition'
+groupName           = 'Gitlab Org'
+
+
+# Add required attributes to body before starting the test.
+# section would be dashboard|group|project
+addBodyAttributes = (section = 'dashboard') ->
+
+  $body = $ 'body'
+
+  $body.removeAttr 'data-page'
+  $body.removeAttr 'data-project'
+  $body.removeAttr 'data-group'
+
+  switch section
+    when 'dashboard'
+      $body.data 'page', 'root:index'
+    when 'group'
+      $body.data 'page', 'groups:show'
+      $body.data 'group', 'gitlab-org'
+    when 'project'
+      $body.data 'page', 'projects:show'
+      $body.data 'project', 'gitlab-ce'
+
+
+# Mock `gl` object in window for dashboard specific page. App code will need it.
+mockDashboardOptions = ->
+
+  window.gl or= {}
+  window.gl.dashboardOptions =
+    issuesPath: dashboardIssuesPath
+    mrPath    : dashboardMRsPath
+
+
+# Mock `gl` object in window for project specific page. App code will need it.
+mockProjectOptions = ->
+
+  window.gl or= {}
+  window.gl.projectOptions =
+    'gitlab-ce'   :
+      issuesPath  : projectIssuesPath
+      mrPath      : projectMRsPath
+      projectName : projectName
+
+
+mockGroupOptions = ->
+
+  window.gl or= {}
+  window.gl.groupOptions =
+    'gitlab-org'  :
+      issuesPath  : groupIssuesPath
+      mrPath      : groupMRsPath
+      projectName : groupName
+
+
+assertLinks = (list, issuesPath, mrsPath) ->
+
+  issuesAssignedToMeLink = "#{issuesPath}/?assignee_id=#{userId}"
+  issuesIHaveCreatedLink = "#{issuesPath}/?author_id=#{userId}"
+  mrsAssignedToMeLink    = "#{mrsPath}/?assignee_id=#{userId}"
+  mrsIHaveCreatedLink    = "#{mrsPath}/?author_id=#{userId}"
+
+  a1 = "a[href='#{issuesAssignedToMeLink}']"
+  a2 = "a[href='#{issuesIHaveCreatedLink}']"
+  a3 = "a[href='#{mrsAssignedToMeLink}']"
+  a4 = "a[href='#{mrsIHaveCreatedLink}']"
+
+  expect(list.find(a1).length).toBe 1
+  expect(list.find(a1).text()).toBe ' Issues assigned to me '
+
+  expect(list.find(a2).length).toBe 1
+  expect(list.find(a2).text()).toBe " Issues I've created "
+
+  expect(list.find(a3).length).toBe 1
+  expect(list.find(a3).text()).toBe ' Merge requests assigned to me '
+
+  expect(list.find(a4).length).toBe 1
+  expect(list.find(a4).text()).toBe " Merge requests I've created "
+
+
+describe 'Search autocomplete dropdown', ->
+
+  fixture.preload 'search_autocomplete.html'
+
+  beforeEach ->
+
+    fixture.load 'search_autocomplete.html'
+    widget = new SearchAutocomplete
+
+
+  it 'should show Dashboard specific dropdown menu', ->
+
+    addBodyAttributes()
+    mockDashboardOptions()
+    widget.searchInput.focus()
+
+    list = widget.wrap.find('.dropdown-menu').find 'ul'
+    assertLinks list, dashboardIssuesPath, dashboardMRsPath
+
+
+  it 'should show Group specific dropdown menu', ->
+
+    addBodyAttributes 'group'
+    mockGroupOptions()
+    widget.searchInput.focus()
+
+    list = widget.wrap.find('.dropdown-menu').find 'ul'
+    assertLinks list, groupIssuesPath, groupMRsPath
+
+
+  it 'should show Project specific dropdown menu', ->
+
+    addBodyAttributes 'project'
+    mockProjectOptions()
+    widget.searchInput.focus()
+
+    list = widget.wrap.find('.dropdown-menu').find 'ul'
+    assertLinks list, projectIssuesPath, projectMRsPath
+
+
+  it 'should not show category related menu if there is text in the input', ->
+
+    addBodyAttributes 'project'
+    mockProjectOptions()
+    widget.searchInput.val 'help'
+    widget.searchInput.focus()
+
+    list = widget.wrap.find('.dropdown-menu').find 'ul'
+    link = "a[href='#{projectIssuesPath}/?assignee_id=#{userId}']"
+    expect(list.find(link).length).toBe 0
diff --git a/spec/javascripts/u2f/authenticate_spec.coffee b/spec/javascripts/u2f/authenticate_spec.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..e8a2892d67889003493d951ffb337f589d030b1e
--- /dev/null
+++ b/spec/javascripts/u2f/authenticate_spec.coffee
@@ -0,0 +1,52 @@
+#= require u2f/authenticate
+#= require u2f/util
+#= require u2f/error
+#= require u2f
+#= require ./mock_u2f_device
+
+describe 'U2FAuthenticate', ->
+  U2FUtil.enableTestMode()
+  fixture.load('u2f/authenticate')
+
+  beforeEach ->
+    @u2fDevice = new MockU2FDevice
+    @container = $("#js-authenticate-u2f")
+    @component = new U2FAuthenticate(@container, {}, "token")
+    @component.start()
+
+  it 'allows authenticating via a U2F device', ->
+    setupButton = @container.find("#js-login-u2f-device")
+    setupMessage = @container.find("p")
+    expect(setupMessage.text()).toContain('Insert your security key')
+    expect(setupButton.text()).toBe('Login Via U2F Device')
+    setupButton.trigger('click')
+
+    inProgressMessage = @container.find("p")
+    expect(inProgressMessage.text()).toContain("Trying to communicate with your device")
+
+    @u2fDevice.respondToAuthenticateRequest({deviceData: "this is data from the device"})
+    authenticatedMessage = @container.find("p")
+    deviceResponse = @container.find('#js-device-response')
+    expect(authenticatedMessage.text()).toContain("Click this button to authenticate with the GitLab server")
+    expect(deviceResponse.val()).toBe('{"deviceData":"this is data from the device"}')
+
+  describe "errors", ->
+    it "displays an error message", ->
+      setupButton = @container.find("#js-login-u2f-device")
+      setupButton.trigger('click')
+      @u2fDevice.respondToAuthenticateRequest({errorCode: "error!"})
+      errorMessage = @container.find("p")
+      expect(errorMessage.text()).toContain("There was a problem communicating with your device")
+
+    it "allows retrying authentication after an error", ->
+      setupButton = @container.find("#js-login-u2f-device")
+      setupButton.trigger('click')
+      @u2fDevice.respondToAuthenticateRequest({errorCode: "error!"})
+      retryButton = @container.find("#js-u2f-try-again")
+      retryButton.trigger('click')
+
+      setupButton = @container.find("#js-login-u2f-device")
+      setupButton.trigger('click')
+      @u2fDevice.respondToAuthenticateRequest({deviceData: "this is data from the device"})
+      authenticatedMessage = @container.find("p")
+      expect(authenticatedMessage.text()).toContain("Click this button to authenticate with the GitLab server")
diff --git a/spec/javascripts/u2f/mock_u2f_device.js.coffee b/spec/javascripts/u2f/mock_u2f_device.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..97ed0e83a0e3287cd3539070779348b081e4a66c
--- /dev/null
+++ b/spec/javascripts/u2f/mock_u2f_device.js.coffee
@@ -0,0 +1,15 @@
+class @MockU2FDevice
+  constructor: () ->
+    window.u2f ||= {}
+
+    window.u2f.register = (appId, registerRequests, signRequests, callback) =>
+      @registerCallback = callback
+
+    window.u2f.sign = (appId, challenges, signRequests, callback) =>
+      @authenticateCallback = callback
+
+  respondToRegisterRequest: (params) =>
+    @registerCallback(params)
+
+  respondToAuthenticateRequest: (params) =>
+    @authenticateCallback(params)
diff --git a/spec/javascripts/u2f/register_spec.js.coffee b/spec/javascripts/u2f/register_spec.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..0858abeca1aab86b940d15714be479dcf64c2459
--- /dev/null
+++ b/spec/javascripts/u2f/register_spec.js.coffee
@@ -0,0 +1,57 @@
+#= require u2f/register
+#= require u2f/util
+#= require u2f/error
+#= require u2f
+#= require ./mock_u2f_device
+
+describe 'U2FRegister', ->
+  U2FUtil.enableTestMode()
+  fixture.load('u2f/register')
+
+  beforeEach ->
+    @u2fDevice = new MockU2FDevice
+    @container = $("#js-register-u2f")
+    @component = new U2FRegister(@container, $("#js-register-u2f-templates"), {}, "token")
+    @component.start()
+
+  it 'allows registering a U2F device', ->
+    setupButton = @container.find("#js-setup-u2f-device")
+    expect(setupButton.text()).toBe('Setup New U2F Device')
+    setupButton.trigger('click')
+
+    inProgressMessage = @container.children("p")
+    expect(inProgressMessage.text()).toContain("Trying to communicate with your device")
+
+    @u2fDevice.respondToRegisterRequest({deviceData: "this is data from the device"})
+    registeredMessage = @container.find('p')
+    deviceResponse = @container.find('#js-device-response')
+    expect(registeredMessage.text()).toContain("Your device was successfully set up!")
+    expect(deviceResponse.val()).toBe('{"deviceData":"this is data from the device"}')
+
+  describe "errors", ->
+    it "doesn't allow the same device to be registered twice (for the same user", ->
+      setupButton = @container.find("#js-setup-u2f-device")
+      setupButton.trigger('click')
+      @u2fDevice.respondToRegisterRequest({errorCode: 4})
+      errorMessage = @container.find("p")
+      expect(errorMessage.text()).toContain("already been registered with us")
+
+    it "displays an error message for other errors", ->
+      setupButton = @container.find("#js-setup-u2f-device")
+      setupButton.trigger('click')
+      @u2fDevice.respondToRegisterRequest({errorCode: "error!"})
+      errorMessage = @container.find("p")
+      expect(errorMessage.text()).toContain("There was a problem communicating with your device")
+
+    it "allows retrying registration after an error", ->
+      setupButton = @container.find("#js-setup-u2f-device")
+      setupButton.trigger('click')
+      @u2fDevice.respondToRegisterRequest({errorCode: "error!"})
+      retryButton = @container.find("#U2FTryAgain")
+      retryButton.trigger('click')
+
+      setupButton = @container.find("#js-setup-u2f-device")
+      setupButton.trigger('click')
+      @u2fDevice.respondToRegisterRequest({deviceData: "this is data from the device"})
+      registeredMessage = @container.find("p")
+      expect(registeredMessage.text()).toContain("Your device was successfully set up!")
diff --git a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
index c2a8ad36c3057fd4acd06cb329f6ead560b288bd..593bd6d5cac994d24bda92071a9d7b5b28e81d30 100644
--- a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
@@ -98,11 +98,6 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
       expect(link).not_to match %r(https?://)
       expect(link).to eq urls.namespace_project_compare_url(project.namespace, project, from: commit1.id, to: commit2.id, only_path: true)
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("See #{reference}")
-      expect(result[:references][:commit_range]).not_to be_empty
-    end
   end
 
   context 'cross-project reference' do
@@ -135,11 +130,6 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
       exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}"
       expect(reference_filter(act).to_html).to eq exp
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("See #{reference}")
-      expect(result[:references][:commit_range]).not_to be_empty
-    end
   end
 
   context 'cross-project URL reference' do
@@ -173,10 +163,5 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
       exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}"
       expect(reference_filter(act).to_html).to eq exp
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("See #{reference}")
-      expect(result[:references][:commit_range]).not_to be_empty
-    end
   end
 end
diff --git a/spec/lib/banzai/filter/commit_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
index 63a32d9d455ea9dd96ba05d88800cbce20f0f7b5..d46d3f1489e40a7c9d9cd8cdddf7843ed7ecfd50 100644
--- a/spec/lib/banzai/filter/commit_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
@@ -93,11 +93,6 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do
       expect(link).not_to match %r(https?://)
       expect(link).to eq urls.namespace_project_commit_url(project.namespace, project, reference, only_path: true)
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("See #{reference}")
-      expect(result[:references][:commit]).not_to be_empty
-    end
   end
 
   context 'cross-project reference' do
@@ -124,11 +119,6 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do
       exp = act = "Committed #{invalidate_reference(reference)}"
       expect(reference_filter(act).to_html).to eq exp
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("See #{reference}")
-      expect(result[:references][:commit]).not_to be_empty
-    end
   end
 
   context 'cross-project URL reference' do
@@ -154,10 +144,5 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do
       act = "Committed #{invalidate_reference(reference)}"
       expect(reference_filter(act).to_html).to match(/<a.+>#{Regexp.escape(invalidate_reference(reference))}<\/a>/)
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("See #{reference}")
-      expect(result[:references][:commit]).not_to be_empty
-    end
   end
 end
diff --git a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
index 92504e171fc3f01929a249d972b24c1dfd1aced9..953466679e4beb276f65b5a1a6adb06f01d91b6f 100644
--- a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
@@ -73,13 +73,5 @@ describe Banzai::Filter::ExternalIssueReferenceFilter, lib: true do
 
       expect(link).to eq helper.url_for_issue("#{reference}", project, only_path: true)
     end
-
-    it 'adds to the results hash' do
-      ext = JiraIssue.new(reference, project)
-
-      result = reference_pipeline_result("Issue #{reference}")
-      expect(result[:references][:external_issue]).not_to be_empty
-      expect(result[:references][:external_issue]).to eq [ext]
-    end
   end
 end
diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
index 266ebef33d6154d571ae9a4e77def5dc0dc0aaf9..8e6a264970d0c6b7d3e9534b9c6fc9c890b03822 100644
--- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
@@ -91,11 +91,6 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
       expect(link).to eq helper.url_for_issue(issue.iid, project, only_path: true)
     end
 
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("Fixed #{reference}")
-      expect(result[:references][:issue]).to eq [issue]
-    end
-
     it 'does not process links containing issue numbers followed by text' do
       href = "#{reference}st"
       doc = reference_filter("<a href='#{href}'></a>")
@@ -136,11 +131,6 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
 
       expect(reference_filter(act).to_html).to eq exp
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("Fixed #{reference}")
-      expect(result[:references][:issue]).to eq [issue]
-    end
   end
 
   context 'cross-project URL reference' do
@@ -160,11 +150,6 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
       doc = reference_filter("Fixed (#{reference}.)")
       expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(issue.to_reference(project))} \(comment 123\)<\/a>\.\)/)
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("Fixed #{reference}")
-      expect(result[:references][:issue]).to eq [issue]
-    end
   end
 
   context 'cross-project reference in link href' do
@@ -184,11 +169,6 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
       doc = reference_filter("Fixed (#{reference}.)")
       expect(doc.to_html).to match(/\(<a.+>Reference<\/a>\.\)/)
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("Fixed #{reference}")
-      expect(result[:references][:issue]).to eq [issue]
-    end
   end
 
   context 'cross-project URL in link href' do
@@ -208,10 +188,5 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
       doc = reference_filter("Fixed (#{reference}.)")
       expect(doc.to_html).to match(/\(<a.+>Reference<\/a>\.\)/)
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("Fixed #{reference}")
-      expect(result[:references][:issue]).to eq [issue]
-    end
   end
 end
diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index b0a38e7c2510b8a01808c9040485525934adacd7..f1064a701d8ca23386b2df446330ca5d8cdd9f09 100644
--- a/spec/lib/banzai/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -48,11 +48,6 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
     expect(link).to eq urls.namespace_project_issues_path(project.namespace, project, label_name: label.name)
   end
 
-  it 'adds to the results hash' do
-    result = reference_pipeline_result("Label #{reference}")
-    expect(result[:references][:label]).to eq [label]
-  end
-
   describe 'label span element' do
     it 'includes default classes' do
       doc = reference_filter("Label #{reference}")
@@ -170,11 +165,6 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
       expect(link).to have_attribute('data-label')
       expect(link.attr('data-label')).to eq label.id.to_s
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("Label #{reference}")
-      expect(result[:references][:label]).to eq [label]
-    end
   end
 
   describe 'cross project label references' do
diff --git a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
index 352710df3071fcc7949ba5ca3452a32eec018786..3185e41fe5c2d2679b5d4137f77ab0ea2164a029 100644
--- a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
@@ -78,11 +78,6 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
       expect(link).not_to match %r(https?://)
       expect(link).to eq urls.namespace_project_merge_request_url(project.namespace, project, merge, only_path: true)
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("Merge #{reference}")
-      expect(result[:references][:merge_request]).to eq [merge]
-    end
   end
 
   context 'cross-project reference' do
@@ -109,11 +104,6 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
 
       expect(reference_filter(act).to_html).to eq exp
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("Merge #{reference}")
-      expect(result[:references][:merge_request]).to eq [merge]
-    end
   end
 
   context 'cross-project URL reference' do
@@ -133,10 +123,5 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
       doc = reference_filter("Merge (#{reference}.)")
       expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(merge.to_reference(project))} \(diffs, comment 123\)<\/a>\.\)/)
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("Merge #{reference}")
-      expect(result[:references][:merge_request]).to eq [merge]
-    end
   end
 end
diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
index bdf48eabb0ec99e0a0320049afc428b97e32676a..23c64d286ddeced14816384ea228f8c80d5b59db 100644
--- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
@@ -48,11 +48,6 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
       namespace_project_milestone_path(project.namespace, project, milestone)
   end
 
-  it 'adds to the results hash' do
-    result = reference_pipeline_result("Milestone #{reference}")
-    expect(result[:references][:milestone]).to eq [milestone]
-  end
-
   context 'Integer-based references' do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
@@ -151,10 +146,31 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
       expect(link).to have_attribute('data-milestone')
       expect(link.attr('data-milestone')).to eq milestone.id.to_s
     end
+  end
+
+  describe 'cross project milestone references' do
+    let(:another_project)  { create(:empty_project, :public) }
+    let(:project_path) { another_project.path_with_namespace }
+    let(:milestone) { create(:milestone, project: another_project) }
+    let(:reference) { milestone.to_reference(project) }
+
+    let!(:result) { reference_filter("See #{reference}") }
+
+    it 'points to referenced project milestone page' do
+      expect(result.css('a').first.attr('href')).to eq urls.
+        namespace_project_milestone_url(another_project.namespace,
+                                        another_project,
+                                        milestone)
+    end
 
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("Milestone #{reference}")
-      expect(result[:references][:milestone]).to eq [milestone]
+    it 'contains cross project content' do
+      expect(result.css('a').first.text).to eq "#{milestone.name} in #{project_path}"
+    end
+
+    it 'escapes the name attribute' do
+      allow_any_instance_of(Milestone).to receive(:title).and_return(%{"></a>whatever<a title="})
+      doc = reference_filter("See #{reference}")
+      expect(doc.css('a').first.text).to eq "#{milestone.name} in #{project_path}"
     end
   end
 
diff --git a/spec/lib/banzai/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb
index c2c2fd0eb6ac3403663268d66ab9fb0b941f87bb..f181125156bf5a6e5994625ddefaffcf5b50caf3 100644
--- a/spec/lib/banzai/filter/redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/redactor_filter_spec.rb
@@ -16,11 +16,23 @@ describe Banzai::Filter::RedactorFilter, lib: true do
   end
 
   context 'with data-project' do
+    let(:parser_class) do
+      Class.new(Banzai::ReferenceParser::BaseParser) do
+        self.reference_type = :test
+      end
+    end
+
+    before do
+      allow(Banzai::ReferenceParser).to receive(:[]).
+        with('test').
+        and_return(parser_class)
+    end
+
     it 'removes unpermitted Project references' do
       user = create(:user)
       project = create(:empty_project)
 
-      link = reference_link(project: project.id, reference_filter: 'ReferenceFilter')
+      link = reference_link(project: project.id, reference_type: 'test')
       doc = filter(link, current_user: user)
 
       expect(doc.css('a').length).to eq 0
@@ -31,14 +43,14 @@ describe Banzai::Filter::RedactorFilter, lib: true do
       project = create(:empty_project)
       project.team << [user, :master]
 
-      link = reference_link(project: project.id, reference_filter: 'ReferenceFilter')
+      link = reference_link(project: project.id, reference_type: 'test')
       doc = filter(link, current_user: user)
 
       expect(doc.css('a').length).to eq 1
     end
 
     it 'handles invalid Project references' do
-      link = reference_link(project: 12345, reference_filter: 'ReferenceFilter')
+      link = reference_link(project: 12345, reference_type: 'test')
 
       expect { filter(link) }.not_to raise_error
     end
@@ -51,18 +63,30 @@ describe Banzai::Filter::RedactorFilter, lib: true do
         project = create(:empty_project, :public)
         issue = create(:issue, :confidential, project: project)
 
-        link = reference_link(project: project.id, issue: issue.id, reference_filter: 'IssueReferenceFilter')
+        link = reference_link(project: project.id, issue: issue.id, reference_type: 'issue')
         doc = filter(link, current_user: non_member)
 
         expect(doc.css('a').length).to eq 0
       end
 
+      it 'removes references for project members with guest role' do
+        member = create(:user)
+        project = create(:empty_project, :public)
+        project.team << [member, :guest]
+        issue = create(:issue, :confidential, project: project)
+
+        link = reference_link(project: project.id, issue: issue.id, reference_type: 'issue')
+        doc = filter(link, current_user: member)
+
+        expect(doc.css('a').length).to eq 0
+      end
+
       it 'allows references for author' do
         author = create(:user)
         project = create(:empty_project, :public)
         issue = create(:issue, :confidential, project: project, author: author)
 
-        link = reference_link(project: project.id, issue: issue.id, reference_filter: 'IssueReferenceFilter')
+        link = reference_link(project: project.id, issue: issue.id, reference_type: 'issue')
         doc = filter(link, current_user: author)
 
         expect(doc.css('a').length).to eq 1
@@ -73,7 +97,7 @@ describe Banzai::Filter::RedactorFilter, lib: true do
         project = create(:empty_project, :public)
         issue = create(:issue, :confidential, project: project, assignee: assignee)
 
-        link = reference_link(project: project.id, issue: issue.id, reference_filter: 'IssueReferenceFilter')
+        link = reference_link(project: project.id, issue: issue.id, reference_type: 'issue')
         doc = filter(link, current_user: assignee)
 
         expect(doc.css('a').length).to eq 1
@@ -85,7 +109,7 @@ describe Banzai::Filter::RedactorFilter, lib: true do
         project.team << [member, :developer]
         issue = create(:issue, :confidential, project: project)
 
-        link = reference_link(project: project.id, issue: issue.id, reference_filter: 'IssueReferenceFilter')
+        link = reference_link(project: project.id, issue: issue.id, reference_type: 'issue')
         doc = filter(link, current_user: member)
 
         expect(doc.css('a').length).to eq 1
@@ -96,7 +120,7 @@ describe Banzai::Filter::RedactorFilter, lib: true do
         project = create(:empty_project, :public)
         issue = create(:issue, :confidential, project: project)
 
-        link = reference_link(project: project.id, issue: issue.id, reference_filter: 'IssueReferenceFilter')
+        link = reference_link(project: project.id, issue: issue.id, reference_type: 'issue')
         doc = filter(link, current_user: admin)
 
         expect(doc.css('a').length).to eq 1
@@ -108,7 +132,7 @@ describe Banzai::Filter::RedactorFilter, lib: true do
       project = create(:empty_project, :public)
       issue = create(:issue, project: project)
 
-      link = reference_link(project: project.id, issue: issue.id, reference_filter: 'IssueReferenceFilter')
+      link = reference_link(project: project.id, issue: issue.id, reference_type: 'issue')
       doc = filter(link, current_user: user)
 
       expect(doc.css('a').length).to eq 1
@@ -121,7 +145,7 @@ describe Banzai::Filter::RedactorFilter, lib: true do
         user = create(:user)
         group = create(:group, :private)
 
-        link = reference_link(group: group.id, reference_filter: 'UserReferenceFilter')
+        link = reference_link(group: group.id, reference_type: 'user')
         doc = filter(link, current_user: user)
 
         expect(doc.css('a').length).to eq 0
@@ -132,14 +156,14 @@ describe Banzai::Filter::RedactorFilter, lib: true do
         group = create(:group, :private)
         group.add_developer(user)
 
-        link = reference_link(group: group.id, reference_filter: 'UserReferenceFilter')
+        link = reference_link(group: group.id, reference_type: 'user')
         doc = filter(link, current_user: user)
 
         expect(doc.css('a').length).to eq 1
       end
 
       it 'handles invalid Group references' do
-        link = reference_link(group: 12345, reference_filter: 'UserReferenceFilter')
+        link = reference_link(group: 12345, reference_type: 'user')
 
         expect { filter(link) }.not_to raise_error
       end
@@ -149,7 +173,7 @@ describe Banzai::Filter::RedactorFilter, lib: true do
       it 'allows any User reference' do
         user = create(:user)
 
-        link = reference_link(user: user.id, reference_filter: 'UserReferenceFilter')
+        link = reference_link(user: user.id, reference_type: 'user')
         doc = filter(link)
 
         expect(doc.css('a').length).to eq 1
diff --git a/spec/lib/banzai/filter/reference_filter_spec.rb b/spec/lib/banzai/filter/reference_filter_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..55e681f6fafd96b482f2868ce31f34062c647258
--- /dev/null
+++ b/spec/lib/banzai/filter/reference_filter_spec.rb
@@ -0,0 +1,45 @@
+require 'spec_helper'
+
+describe Banzai::Filter::ReferenceFilter, lib: true do
+  let(:project) { build(:project) }
+
+  describe '#each_node' do
+    it 'iterates over the nodes in a document' do
+      document = Nokogiri::HTML.fragment('<a href="foo">foo</a>')
+      filter = described_class.new(document, project: project)
+
+      expect { |b| filter.each_node(&b) }.
+        to yield_with_args(an_instance_of(Nokogiri::XML::Element))
+    end
+
+    it 'returns an Enumerator when no block is given' do
+      document = Nokogiri::HTML.fragment('<a href="foo">foo</a>')
+      filter = described_class.new(document, project: project)
+
+      expect(filter.each_node).to be_an_instance_of(Enumerator)
+    end
+
+    it 'skips links with a "gfm" class' do
+      document = Nokogiri::HTML.fragment('<a href="foo" class="gfm">foo</a>')
+      filter = described_class.new(document, project: project)
+
+      expect { |b| filter.each_node(&b) }.not_to yield_control
+    end
+
+    it 'skips text nodes in pre elements' do
+      document = Nokogiri::HTML.fragment('<pre>foo</pre>')
+      filter = described_class.new(document, project: project)
+
+      expect { |b| filter.each_node(&b) }.not_to yield_control
+    end
+  end
+
+  describe '#nodes' do
+    it 'returns an Array of the HTML nodes' do
+      document = Nokogiri::HTML.fragment('<a href="foo">foo</a>')
+      filter = described_class.new(document, project: project)
+
+      expect(filter.nodes).to eq([document.children[0]])
+    end
+  end
+end
diff --git a/spec/lib/banzai/filter/reference_gatherer_filter_spec.rb b/spec/lib/banzai/filter/reference_gatherer_filter_spec.rb
deleted file mode 100644
index c8b1dfdf9448d5e4a5df45968e7db0d8c0e0b2d5..0000000000000000000000000000000000000000
--- a/spec/lib/banzai/filter/reference_gatherer_filter_spec.rb
+++ /dev/null
@@ -1,87 +0,0 @@
-require 'spec_helper'
-
-describe Banzai::Filter::ReferenceGathererFilter, lib: true do
-  include ActionView::Helpers::UrlHelper
-  include FilterSpecHelper
-
-  def reference_link(data)
-    link_to('text', '', class: 'gfm', data: data)
-  end
-
-  context "for issue references" do
-
-    context 'with data-project' do
-      it 'removes unpermitted Project references' do
-        user = create(:user)
-        project = create(:empty_project)
-        issue = create(:issue, project: project)
-
-        link = reference_link(project: project.id, issue: issue.id, reference_filter: 'IssueReferenceFilter')
-        result = pipeline_result(link, current_user: user)
-
-        expect(result[:references][:issue]).to be_empty
-      end
-
-      it 'allows permitted Project references' do
-        user = create(:user)
-        project = create(:empty_project)
-        issue = create(:issue, project: project)
-        project.team << [user, :master]
-
-        link = reference_link(project: project.id, issue: issue.id, reference_filter: 'IssueReferenceFilter')
-        result = pipeline_result(link, current_user: user)
-
-        expect(result[:references][:issue]).to eq([issue])
-      end
-
-      it 'handles invalid Project references' do
-        link = reference_link(project: 12345, issue: 12345, reference_filter: 'IssueReferenceFilter')
-
-        expect { pipeline_result(link) }.not_to raise_error
-      end
-    end
-  end
-
-  context "for user references" do
-
-    context 'with data-group' do
-      it 'removes unpermitted Group references' do
-        user = create(:user)
-        group = create(:group)
-
-        link = reference_link(group: group.id, reference_filter: 'UserReferenceFilter')
-        result = pipeline_result(link, current_user: user)
-
-        expect(result[:references][:user]).to be_empty
-      end
-
-      it 'allows permitted Group references' do
-        user = create(:user)
-        group = create(:group)
-        group.add_developer(user)
-
-        link = reference_link(group: group.id, reference_filter: 'UserReferenceFilter')
-        result = pipeline_result(link, current_user: user)
-
-        expect(result[:references][:user]).to eq([user])
-      end
-
-      it 'handles invalid Group references' do
-        link = reference_link(group: 12345, reference_filter: 'UserReferenceFilter')
-
-        expect { pipeline_result(link) }.not_to raise_error
-      end
-    end
-
-    context 'with data-user' do
-      it 'allows any User reference' do
-        user = create(:user)
-
-        link = reference_link(user: user.id, reference_filter: 'UserReferenceFilter')
-        result = pipeline_result(link)
-
-        expect(result[:references][:user]).to eq([user])
-      end
-    end
-  end
-end
diff --git a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
index 26466fbb180138b8ce28755faf51d35bc8cb0ecf..5068ddd7faa5c9f8537830c88617b8d043360e5d 100644
--- a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
@@ -77,11 +77,6 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do
       expect(link).not_to match %r(https?://)
       expect(link).to eq urls.namespace_project_snippet_url(project.namespace, project, snippet, only_path: true)
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("Snippet #{reference}")
-      expect(result[:references][:snippet]).to eq [snippet]
-    end
   end
 
   context 'cross-project reference' do
@@ -107,11 +102,6 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do
 
       expect(reference_filter(act).to_html).to eq exp
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("Snippet #{reference}")
-      expect(result[:references][:snippet]).to eq [snippet]
-    end
   end
 
   context 'cross-project URL reference' do
@@ -137,10 +127,5 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do
 
       expect(reference_filter(act).to_html).to match(/<a.+>#{Regexp.escape(invalidate_reference(reference))}<\/a>/)
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("Snippet #{reference}")
-      expect(result[:references][:snippet]).to eq [snippet]
-    end
   end
 end
diff --git a/spec/lib/banzai/filter/user_reference_filter_spec.rb b/spec/lib/banzai/filter/user_reference_filter_spec.rb
index 8bdebae1841bdf6782c8ff98199ffda81bf16cf6..108b36a97cc8e81cf53a05b14ded6c752cdcddd6 100644
--- a/spec/lib/banzai/filter/user_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/user_reference_filter_spec.rb
@@ -31,28 +31,22 @@ describe Banzai::Filter::UserReferenceFilter, lib: true do
     end
 
     it 'supports a special @all mention' do
-      doc = reference_filter("Hey #{reference}")
+      doc = reference_filter("Hey #{reference}", author: user)
       expect(doc.css('a').length).to eq 1
       expect(doc.css('a').first.attr('href'))
         .to eq urls.namespace_project_url(project.namespace, project)
     end
 
-    context "when the author is a member of the project" do
+    it 'includes a data-author attribute when there is an author' do
+      doc = reference_filter(reference, author: user)
 
-      it 'adds to the results hash' do
-        result = reference_pipeline_result("Hey #{reference}", author: project.creator)
-        expect(result[:references][:user]).to eq [project.creator]
-      end
+      expect(doc.css('a').first.attr('data-author')).to eq(user.id.to_s)
     end
 
-    context "when the author is not a member of the project" do
-
-      let(:other_user) { create(:user) }
+    it 'does not include a data-author attribute when there is no author' do
+      doc = reference_filter(reference)
 
-      it "doesn't add to the results hash" do
-        result = reference_pipeline_result("Hey #{reference}", author: other_user)
-        expect(result[:references][:user]).to eq []
-      end
+      expect(doc.css('a').first.has_attribute?('data-author')).to eq(false)
     end
   end
 
@@ -83,11 +77,6 @@ describe Banzai::Filter::UserReferenceFilter, lib: true do
       expect(link).to have_attribute('data-user')
       expect(link.attr('data-user')).to eq user.namespace.owner_id.to_s
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("Hey #{reference}")
-      expect(result[:references][:user]).to eq [user]
-    end
   end
 
   context 'mentioning a group' do
@@ -106,11 +95,6 @@ describe Banzai::Filter::UserReferenceFilter, lib: true do
       expect(link).to have_attribute('data-group')
       expect(link.attr('data-group')).to eq group.id.to_s
     end
-
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("Hey #{reference}")
-      expect(result[:references][:user]).to eq group.users
-    end
   end
 
   it 'links with adjacent text' do
@@ -151,10 +135,24 @@ describe Banzai::Filter::UserReferenceFilter, lib: true do
       expect(link).to have_attribute('data-user')
       expect(link.attr('data-user')).to eq user.namespace.owner_id.to_s
     end
+  end
+
+  describe '#namespaces' do
+    it 'returns a Hash containing all Namespaces' do
+      document = Nokogiri::HTML.fragment("<p>#{user.to_reference}</p>")
+      filter = described_class.new(document, project: project)
+      ns = user.namespace
+
+      expect(filter.namespaces).to eq({ ns.path => ns })
+    end
+  end
+
+  describe '#usernames' do
+    it 'returns the usernames mentioned in a document' do
+      document = Nokogiri::HTML.fragment("<p>#{user.to_reference}</p>")
+      filter = described_class.new(document, project: project)
 
-    it 'adds to the results hash' do
-      result = reference_pipeline_result("Hey #{reference}")
-      expect(result[:references][:user]).to eq [user]
+      expect(filter.usernames).to eq([user.username])
     end
   end
 end
diff --git a/spec/lib/banzai/filter/wiki_link_filter_spec.rb b/spec/lib/banzai/filter/wiki_link_filter_spec.rb
deleted file mode 100644
index 185abbb2108041e99f5d76b219165f961e7bf763..0000000000000000000000000000000000000000
--- a/spec/lib/banzai/filter/wiki_link_filter_spec.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-require 'spec_helper'
-
-describe Banzai::Filter::WikiLinkFilter, lib: true do
-  include FilterSpecHelper
-
-  let(:namespace) { build_stubbed(:namespace, name: "wiki_link_ns") }
-  let(:project)   { build_stubbed(:empty_project, :public, name: "wiki_link_project", namespace: namespace) }
-  let(:user) { double }
-  let(:project_wiki) { ProjectWiki.new(project, user) }
-
-  describe "links within the wiki (relative)" do
-    describe "hierarchical links to the current directory" do
-      it "doesn't rewrite non-file links" do
-        link = "<a href='./page'>Link to Page</a>"
-        filtered_link = filter(link, project_wiki: project_wiki).children[0]
-
-        expect(filtered_link.attribute('href').value).to eq('./page')
-      end
-
-      it "doesn't rewrite file links" do
-        link = "<a href='./page.md'>Link to Page</a>"
-        filtered_link = filter(link, project_wiki: project_wiki).children[0]
-
-        expect(filtered_link.attribute('href').value).to eq('./page.md')
-      end
-    end
-
-    describe "hierarchical links to the parent directory" do
-      it "doesn't rewrite non-file links" do
-        link = "<a href='../page'>Link to Page</a>"
-        filtered_link = filter(link, project_wiki: project_wiki).children[0]
-
-        expect(filtered_link.attribute('href').value).to eq('../page')
-      end
-
-      it "doesn't rewrite file links" do
-        link = "<a href='../page.md'>Link to Page</a>"
-        filtered_link = filter(link, project_wiki: project_wiki).children[0]
-
-        expect(filtered_link.attribute('href').value).to eq('../page.md')
-      end
-    end
-
-    describe "hierarchical links to a sub-directory" do
-      it "doesn't rewrite non-file links" do
-        link = "<a href='./subdirectory/page'>Link to Page</a>"
-        filtered_link = filter(link, project_wiki: project_wiki).children[0]
-
-        expect(filtered_link.attribute('href').value).to eq('./subdirectory/page')
-      end
-
-      it "doesn't rewrite file links" do
-        link = "<a href='./subdirectory/page.md'>Link to Page</a>"
-        filtered_link = filter(link, project_wiki: project_wiki).children[0]
-
-        expect(filtered_link.attribute('href').value).to eq('./subdirectory/page.md')
-      end
-    end
-
-    describe "non-hierarchical links" do
-      it 'rewrites non-file links to be at the scope of the wiki root' do
-        link = "<a href='page'>Link to Page</a>"
-        filtered_link = filter(link, project_wiki: project_wiki).children[0]
-
-        expect(filtered_link.attribute('href').value).to match('/wiki_link_ns/wiki_link_project/wikis/page')
-      end
-
-      it "doesn't rewrite file links" do
-        link = "<a href='page.md'>Link to Page</a>"
-        filtered_link = filter(link, project_wiki: project_wiki).children[0]
-
-        expect(filtered_link.attribute('href').value).to eq('page.md')
-      end
-    end
-  end
-
-  describe "links outside the wiki (absolute)" do
-    it "doesn't rewrite links" do
-      link = "<a href='http://example.com/page'>Link to Page</a>"
-      filtered_link = filter(link, project_wiki: project_wiki).children[0]
-
-      expect(filtered_link.attribute('href').value).to eq('http://example.com/page')
-    end
-  end
-end
diff --git a/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb b/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb
index 7aa1b4a3bf69d955eed0c0212073445978698884..72bc6a0b704fff34fcbc295c5bb41eae58d0d1c7 100644
--- a/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb
@@ -50,4 +50,112 @@ describe Banzai::Pipeline::WikiPipeline do
       end
     end
   end
+
+  describe "Links" do
+    let(:namespace) { create(:namespace, name: "wiki_link_ns") }
+    let(:project)   { create(:empty_project, :public, name: "wiki_link_project", namespace: namespace) }
+    let(:project_wiki) { ProjectWiki.new(project, double(:user)) }
+    let(:page) { build(:wiki_page, wiki: project_wiki, page: OpenStruct.new(url_path: 'nested/twice/start-page')) }
+
+    { "when GitLab is hosted at a root URL" => '/',
+      "when GitLab is hosted at a relative URL" => '/nested/relative/gitlab' }.each do |test_name, relative_url_root|
+
+      context test_name do
+        before do
+          allow(Gitlab.config.gitlab).to receive(:relative_url_root).and_return(relative_url_root)
+        end
+
+        describe "linking to pages within the wiki" do
+          context "when creating hierarchical links to the current directory" do
+            it "rewrites non-file links to be at the scope of the current directory" do
+              markdown = "[Page](./page)"
+              output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
+
+              expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/page\"")
+            end
+
+            it "rewrites file links to be at the scope of the current directory" do
+              markdown = "[Link to Page](./page.md)"
+              output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
+
+              expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/page.md\"")
+            end
+          end
+
+          context "when creating hierarchical links to the parent directory" do
+            it "rewrites non-file links to be at the scope of the parent directory" do
+              markdown = "[Link to Page](../page)"
+              output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
+
+              expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/page\"")
+            end
+
+            it "rewrites file links to be at the scope of the parent directory" do
+              markdown = "[Link to Page](../page.md)"
+              output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
+
+              expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/page.md\"")
+            end
+          end
+
+          context "when creating hierarchical links to a sub-directory" do
+            it "rewrites non-file links to be at the scope of the sub-directory" do
+              markdown = "[Link to Page](./subdirectory/page)"
+              output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
+
+              expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/subdirectory/page\"")
+            end
+
+            it "rewrites file links to be at the scope of the sub-directory" do
+              markdown = "[Link to Page](./subdirectory/page.md)"
+              output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
+
+              expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/subdirectory/page.md\"")
+            end
+          end
+
+          describe "when creating non-hierarchical links" do
+            it 'rewrites non-file links to be at the scope of the wiki root' do
+              markdown = "[Link to Page](page)"
+              output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
+
+              expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/page\"")
+            end
+
+            it "rewrites file links to be at the scope of the current directory" do
+              markdown = "[Link to Page](page.md)"
+              output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
+
+              expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/page.md\"")
+            end
+          end
+
+          describe "when creating root links" do
+            it 'rewrites non-file links to be at the scope of the wiki root' do
+              markdown = "[Link to Page](/page)"
+              output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
+
+              expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/page\"")
+            end
+
+            it 'rewrites file links to be at the scope of the wiki root' do
+              markdown = "[Link to Page](/page.md)"
+              output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
+
+              expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/page.md\"")
+            end
+          end
+        end
+
+        describe "linking to pages outside the wiki (absolute)" do
+          it "doesn't rewrite links" do
+            markdown = "[Link to Page](http://example.com/page)"
+            output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
+
+            expect(output).to include('href="http://example.com/page"')
+          end
+        end
+      end
+    end
+  end
 end
diff --git a/spec/lib/banzai/reference_parser/base_parser_spec.rb b/spec/lib/banzai/reference_parser/base_parser_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..543b4786d842192d7eea8f461a491cfed0e84a8d
--- /dev/null
+++ b/spec/lib/banzai/reference_parser/base_parser_spec.rb
@@ -0,0 +1,237 @@
+require 'spec_helper'
+
+describe Banzai::ReferenceParser::BaseParser, lib: true do
+  include ReferenceParserHelpers
+
+  let(:user) { create(:user) }
+  let(:project) { create(:empty_project, :public) }
+
+  subject do
+    klass = Class.new(described_class) do
+      self.reference_type = :foo
+    end
+
+    klass.new(project, user)
+  end
+
+  describe '.reference_type=' do
+    it 'sets the reference type' do
+      dummy = Class.new(described_class)
+      dummy.reference_type = :foo
+
+      expect(dummy.reference_type).to eq(:foo)
+    end
+  end
+
+  describe '#nodes_visible_to_user' do
+    let(:link) { empty_html_link }
+
+    context 'when the link has a data-project attribute' do
+      it 'returns the nodes if the attribute value equals the current project ID' do
+        link['data-project'] = project.id.to_s
+
+        expect(Ability.abilities).not_to receive(:allowed?)
+        expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
+      end
+
+      it 'returns the nodes if the user can read the project' do
+        other_project = create(:empty_project, :public)
+
+        link['data-project'] = other_project.id.to_s
+
+        expect(Ability.abilities).to receive(:allowed?).
+          with(user, :read_project, other_project).
+          and_return(true)
+
+        expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
+      end
+
+      it 'returns an empty Array when the attribute value is empty' do
+        link['data-project'] = ''
+
+        expect(subject.nodes_visible_to_user(user, [link])).to eq([])
+      end
+
+      it 'returns an empty Array when the user can not read the project' do
+        other_project = create(:empty_project, :public)
+
+        link['data-project'] = other_project.id.to_s
+
+        expect(Ability.abilities).to receive(:allowed?).
+          with(user, :read_project, other_project).
+          and_return(false)
+
+        expect(subject.nodes_visible_to_user(user, [link])).to eq([])
+      end
+    end
+
+    context 'when the link does not have a data-project attribute' do
+      it 'returns the nodes' do
+        expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
+      end
+    end
+  end
+
+  describe '#nodes_user_can_reference' do
+    it 'returns the nodes' do
+      link = double(:link)
+
+      expect(subject.nodes_user_can_reference(user, [link])).to eq([link])
+    end
+  end
+
+  describe '#referenced_by' do
+    context 'when references_relation is implemented' do
+      it 'returns a collection of objects' do
+        links = Nokogiri::HTML.fragment("<a data-foo='#{user.id}'></a>").
+          children
+
+        expect(subject).to receive(:references_relation).and_return(User)
+        expect(subject.referenced_by(links)).to eq([user])
+      end
+    end
+
+    context 'when references_relation is not implemented' do
+      it 'raises NotImplementedError' do
+        links = Nokogiri::HTML.fragment('<a data-foo="1"></a>').children
+
+        expect { subject.referenced_by(links) }.
+          to raise_error(NotImplementedError)
+      end
+    end
+  end
+
+  describe '#references_relation' do
+    it 'raises NotImplementedError' do
+      expect { subject.references_relation }.to raise_error(NotImplementedError)
+    end
+  end
+
+  describe '#gather_attributes_per_project' do
+    it 'returns a Hash containing attribute values per project' do
+      link = Nokogiri::HTML.fragment('<a data-project="1" data-foo="2"></a>').
+        children[0]
+
+      hash = subject.gather_attributes_per_project([link], 'data-foo')
+
+      expect(hash).to be_an_instance_of(Hash)
+
+      expect(hash[1].to_a).to eq(['2'])
+    end
+  end
+
+  describe '#grouped_objects_for_nodes' do
+    it 'returns a Hash grouping objects per ID' do
+      nodes = [double(:node)]
+
+      expect(subject).to receive(:unique_attribute_values).
+        with(nodes, 'data-user').
+        and_return([user.id])
+
+      hash = subject.grouped_objects_for_nodes(nodes, User, 'data-user')
+
+      expect(hash).to eq({ user.id => user })
+    end
+
+    it 'returns an empty Hash when the list of nodes is empty' do
+      expect(subject.grouped_objects_for_nodes([], User, 'data-user')).to eq({})
+    end
+  end
+
+  describe '#unique_attribute_values' do
+    it 'returns an Array of unique values' do
+      link = double(:link)
+
+      expect(link).to receive(:has_attribute?).
+        with('data-foo').
+        twice.
+        and_return(true)
+
+      expect(link).to receive(:attr).
+        with('data-foo').
+        twice.
+        and_return('1')
+
+      nodes = [link, link]
+
+      expect(subject.unique_attribute_values(nodes, 'data-foo')).to eq(['1'])
+    end
+  end
+
+  describe '#process' do
+    it 'gathers the references for every node matching the reference type' do
+      dummy = Class.new(described_class) do
+        self.reference_type = :test
+      end
+
+      instance = dummy.new(project, user)
+      document = Nokogiri::HTML.fragment('<a class="gfm"></a><a class="gfm" data-reference-type="test"></a>')
+
+      expect(instance).to receive(:gather_references).
+        with([document.children[1]]).
+        and_return([user])
+
+      expect(instance.process([document])).to eq([user])
+    end
+  end
+
+  describe '#gather_references' do
+    let(:link) { double(:link) }
+
+    it 'does not process links a user can not reference' do
+      expect(subject).to receive(:nodes_user_can_reference).
+        with(user, [link]).
+        and_return([])
+
+      expect(subject).to receive(:referenced_by).with([])
+
+      subject.gather_references([link])
+    end
+
+    it 'does not process links a user can not see' do
+      expect(subject).to receive(:nodes_user_can_reference).
+        with(user, [link]).
+        and_return([link])
+
+      expect(subject).to receive(:nodes_visible_to_user).
+        with(user, [link]).
+        and_return([])
+
+      expect(subject).to receive(:referenced_by).with([])
+
+      subject.gather_references([link])
+    end
+
+    it 'returns the references if a user can reference and see a link' do
+      expect(subject).to receive(:nodes_user_can_reference).
+        with(user, [link]).
+        and_return([link])
+
+      expect(subject).to receive(:nodes_visible_to_user).
+        with(user, [link]).
+        and_return([link])
+
+      expect(subject).to receive(:referenced_by).with([link])
+
+      subject.gather_references([link])
+    end
+  end
+
+  describe '#can?' do
+    it 'delegates the permissions check to the Ability class' do
+      user = double(:user)
+
+      expect(Ability.abilities).to receive(:allowed?).
+        with(user, :read_project, project)
+
+      subject.can?(user, :read_project, project)
+    end
+  end
+
+  describe '#find_projects_for_hash_keys' do
+    it 'returns a list of Projects' do
+      expect(subject.find_projects_for_hash_keys(project.id => project)).
+        to eq([project])
+    end
+  end
+end
diff --git a/spec/lib/banzai/reference_parser/commit_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_parser_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0b76d29fce019d36517fe96bb96355118011042f
--- /dev/null
+++ b/spec/lib/banzai/reference_parser/commit_parser_spec.rb
@@ -0,0 +1,113 @@
+require 'spec_helper'
+
+describe Banzai::ReferenceParser::CommitParser, lib: true do
+  include ReferenceParserHelpers
+
+  let(:project) { create(:empty_project, :public) }
+  let(:user) { create(:user) }
+  subject { described_class.new(project, user) }
+  let(:link) { empty_html_link }
+
+  describe '#referenced_by' do
+    context 'when the link has a data-project attribute' do
+      before do
+        link['data-project'] = project.id.to_s
+      end
+
+      context 'when the link has a data-commit attribute' do
+        before do
+          link['data-commit'] = '123'
+        end
+
+        it 'returns an Array of commits' do
+          commit = double(:commit)
+
+          allow_any_instance_of(Project).to receive(:valid_repo?).
+            and_return(true)
+
+          expect(subject).to receive(:find_commits).
+            with(project, ['123']).
+            and_return([commit])
+
+          expect(subject.referenced_by([link])).to eq([commit])
+        end
+
+        it 'returns an empty Array when the commit could not be found' do
+          allow_any_instance_of(Project).to receive(:valid_repo?).
+            and_return(true)
+
+          expect(subject).to receive(:find_commits).
+            with(project, ['123']).
+            and_return([])
+
+          expect(subject.referenced_by([link])).to eq([])
+        end
+
+        it 'skips projects without valid repositories' do
+          allow_any_instance_of(Project).to receive(:valid_repo?).
+            and_return(false)
+
+          expect(subject.referenced_by([link])).to eq([])
+        end
+      end
+
+      context 'when the link does not have a data-commit attribute' do
+        it 'returns an empty Array' do
+          allow_any_instance_of(Project).to receive(:valid_repo?).
+            and_return(true)
+
+          expect(subject.referenced_by([link])).to eq([])
+        end
+      end
+    end
+
+    context 'when the link does not have a data-project attribute' do
+      it 'returns an empty Array' do
+        allow_any_instance_of(Project).to receive(:valid_repo?).
+          and_return(true)
+
+        expect(subject.referenced_by([link])).to eq([])
+      end
+    end
+  end
+
+  describe '#commit_ids_per_project' do
+    before do
+      link['data-project'] = project.id.to_s
+    end
+
+    it 'returns a Hash containing commit IDs per project' do
+      link['data-commit'] = '123'
+
+      hash = subject.commit_ids_per_project([link])
+
+      expect(hash).to be_an_instance_of(Hash)
+
+      expect(hash[project.id].to_a).to eq(['123'])
+    end
+
+    it 'does not add a project when the data-commit attribute is empty' do
+      hash = subject.commit_ids_per_project([link])
+
+      expect(hash).to be_empty
+    end
+  end
+
+  describe '#find_commits' do
+    it 'returns an Array of commit objects' do
+      commit = double(:commit)
+
+      expect(project).to receive(:commit).with('123').and_return(commit)
+      expect(project).to receive(:valid_repo?).and_return(true)
+
+      expect(subject.find_commits(project, %w{123})).to eq([commit])
+    end
+
+    it 'skips commit IDs for which no commit could be found' do
+      expect(project).to receive(:commit).with('123').and_return(nil)
+      expect(project).to receive(:valid_repo?).and_return(true)
+
+      expect(subject.find_commits(project, %w{123})).to eq([])
+    end
+  end
+end
diff --git a/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ba982f38542745199074e5362eb0d3d8e8651492
--- /dev/null
+++ b/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb
@@ -0,0 +1,120 @@
+require 'spec_helper'
+
+describe Banzai::ReferenceParser::CommitRangeParser, lib: true do
+  include ReferenceParserHelpers
+
+  let(:project) { create(:empty_project, :public) }
+  let(:user) { create(:user) }
+  subject { described_class.new(project, user) }
+  let(:link) { empty_html_link }
+
+  describe '#referenced_by' do
+    context 'when the link has a data-project attribute' do
+      before do
+        link['data-project'] = project.id.to_s
+      end
+
+      context 'when the link as a data-commit-range attribute' do
+        before do
+          link['data-commit-range'] = '123..456'
+        end
+
+        it 'returns an Array of commit ranges' do
+          range = double(:range)
+
+          expect(subject).to receive(:find_object).
+            with(project, '123..456').
+            and_return(range)
+
+          expect(subject.referenced_by([link])).to eq([range])
+        end
+
+        it 'returns an empty Array when the commit range could not be found' do
+          expect(subject).to receive(:find_object).
+            with(project, '123..456').
+            and_return(nil)
+
+          expect(subject.referenced_by([link])).to eq([])
+        end
+      end
+
+      context 'when the link does not have a data-commit-range attribute' do
+        it 'returns an empty Array' do
+          expect(subject.referenced_by([link])).to eq([])
+        end
+      end
+    end
+
+    context 'when the link does not have a data-project attribute' do
+      it 'returns an empty Array' do
+        expect(subject.referenced_by([link])).to eq([])
+      end
+    end
+  end
+
+  describe '#commit_range_ids_per_project' do
+    before do
+      link['data-project'] = project.id.to_s
+    end
+
+    it 'returns a Hash containing range IDs per project' do
+      link['data-commit-range'] = '123..456'
+
+      hash = subject.commit_range_ids_per_project([link])
+
+      expect(hash).to be_an_instance_of(Hash)
+
+      expect(hash[project.id].to_a).to eq(['123..456'])
+    end
+
+    it 'does not add a project when the data-commit-range attribute is empty' do
+      hash = subject.commit_range_ids_per_project([link])
+
+      expect(hash).to be_empty
+    end
+  end
+
+  describe '#find_ranges' do
+    it 'returns an Array of range objects' do
+      range = double(:commit)
+
+      expect(subject).to receive(:find_object).
+        with(project, '123..456').
+        and_return(range)
+
+      expect(subject.find_ranges(project, ['123..456'])).to eq([range])
+    end
+
+    it 'skips ranges that could not be found' do
+      expect(subject).to receive(:find_object).
+        with(project, '123..456').
+        and_return(nil)
+
+      expect(subject.find_ranges(project, ['123..456'])).to eq([])
+    end
+  end
+
+  describe '#find_object' do
+    let(:range) { double(:range) }
+
+    before do
+      expect(CommitRange).to receive(:new).and_return(range)
+    end
+
+    context 'when the range has valid commits' do
+      it 'returns the commit range' do
+        expect(range).to receive(:valid_commits?).and_return(true)
+
+        expect(subject.find_object(project, '123..456')).to eq(range)
+      end
+    end
+
+    context 'when the range does not have any valid commits' do
+      it 'returns nil' do
+        expect(range).to receive(:valid_commits?).and_return(false)
+
+        expect(subject.find_object(project, '123..456')).to be_nil
+      end
+    end
+  end
+end
diff --git a/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb b/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a6ef8394fe7a98dee317f1811c229bfe5651f142
--- /dev/null
+++ b/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb
@@ -0,0 +1,62 @@
+require 'spec_helper'
+
+describe Banzai::ReferenceParser::ExternalIssueParser, lib: true do
+  include ReferenceParserHelpers
+
+  let(:project) { create(:empty_project, :public) }
+  let(:user) { create(:user) }
+  subject { described_class.new(project, user) }
+  let(:link) { empty_html_link }
+
+  describe '#referenced_by' do
+    context 'when the link has a data-project attribute' do
+      before do
+        link['data-project'] = project.id.to_s
+      end
+
+      context 'when the link has a data-external-issue attribute' do
+        it 'returns an Array of ExternalIssue instances' do
+          link['data-external-issue'] = '123'
+
+          refs = subject.referenced_by([link])
+
+          expect(refs).to eq([ExternalIssue.new('123', project)])
+        end
+      end
+
+      context 'when the link does not have a data-external-issue attribute' do
+        it 'returns an empty Array' do
+          expect(subject.referenced_by([link])).to eq([])
+        end
+      end
+    end
+
+    context 'when the link does not have a data-project attribute' do
+      it 'returns an empty Array' do
+        expect(subject.referenced_by([link])).to eq([])
+      end
+    end
+  end
+
+  describe '#issue_ids_per_project' do
+    before do
+      link['data-project'] = project.id.to_s
+    end
+
+    it 'returns a Hash containing range IDs per project' do
+      link['data-external-issue'] = '123'
+
+      hash = subject.issue_ids_per_project([link])
+
+      expect(hash).to be_an_instance_of(Hash)
+
+      expect(hash[project.id].to_a).to eq(['123'])
+    end
+
+    it 'does not add a project when the data-external-issue attribute is empty' do
+      hash = subject.issue_ids_per_project([link])
+
+      expect(hash).to be_empty
+    end
+  end
+end
diff --git a/spec/lib/banzai/reference_parser/issue_parser_spec.rb b/spec/lib/banzai/reference_parser/issue_parser_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..514c752546d83f722225f4b5491180239d24748c
--- /dev/null
+++ b/spec/lib/banzai/reference_parser/issue_parser_spec.rb
@@ -0,0 +1,79 @@
+require 'spec_helper'
+
+describe Banzai::ReferenceParser::IssueParser, lib: true do
+  include ReferenceParserHelpers
+
+  let(:project) { create(:empty_project, :public) }
+  let(:user) { create(:user) }
+  let(:issue) { create(:issue, project: project) }
+  subject { described_class.new(project, user) }
+  let(:link) { empty_html_link }
+
+  describe '#nodes_visible_to_user' do
+    context 'when the link has a data-issue attribute' do
+      before do
+        link['data-issue'] = issue.id.to_s
+      end
+
+      it 'returns the nodes when the user can read the issue' do
+        expect(Ability.abilities).to receive(:allowed?).
+          with(user, :read_issue, issue).
+          and_return(true)
+
+        expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
+      end
+
+      it 'returns an empty Array when the user can not read the issue' do
+        expect(Ability.abilities).to receive(:allowed?).
+          with(user, :read_issue, issue).
+          and_return(false)
+
+        expect(subject.nodes_visible_to_user(user, [link])).to eq([])
+      end
+    end
+
+    context 'when the link does not have a data-issue attribute' do
+      it 'returns an empty Array' do
+        expect(subject.nodes_visible_to_user(user, [link])).to eq([])
+      end
+    end
+
+    context 'when the project uses an external issue tracker' do
+      it 'returns all nodes' do
+        link = double(:link)
+
+        expect(project).to receive(:external_issue_tracker).and_return(true)
+
+        expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
+      end
+    end
+  end
+
+  describe '#referenced_by' do
+    context 'when the link has a data-issue attribute' do
+      context 'using an existing issue ID' do
+        before do
+          link['data-issue'] = issue.id.to_s
+        end
+
+        it 'returns an Array of issues' do
+          expect(subject.referenced_by([link])).to eq([issue])
+        end
+
+        it 'returns an empty Array when the list of nodes is empty' do
+          expect(subject.referenced_by([link])).to eq([issue])
+          expect(subject.referenced_by([])).to eq([])
+        end
+      end
+    end
+  end
+
+  describe '#issues_for_nodes' do
+    it 'returns a Hash containing the issues for a list of nodes' do
+      link['data-issue'] = issue.id.to_s
+      nodes = [link]
+
+      expect(subject.issues_for_nodes(nodes)).to eq({ issue.id => issue })
+    end
+  end
+end
diff --git a/spec/lib/banzai/reference_parser/label_parser_spec.rb b/spec/lib/banzai/reference_parser/label_parser_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..77fda47f0e7c9f8e884d30e68187d37a2eb05b9d
--- /dev/null
+++ b/spec/lib/banzai/reference_parser/label_parser_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe Banzai::ReferenceParser::LabelParser, lib: true do
+  include ReferenceParserHelpers
+
+  let(:project) { create(:empty_project, :public) }
+  let(:user) { create(:user) }
+  let(:label) { create(:label, project: project) }
+  subject { described_class.new(project, user) }
+  let(:link) { empty_html_link }
+
+  describe '#referenced_by' do
+    describe 'when the link has a data-label attribute' do
+      context 'using an existing label ID' do
+        it 'returns an Array of labels' do
+          link['data-label'] = label.id.to_s
+
+          expect(subject.referenced_by([link])).to eq([label])
+        end
+      end
+
+      context 'using a non-existing label ID' do
+        it 'returns an empty Array' do
+          link['data-label'] = ''
+
+          expect(subject.referenced_by([link])).to eq([])
+        end
+      end
+    end
+  end
+end
diff --git a/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb b/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cf89ad598ea5203a6bbf6f49e8885b5c0e017a10
--- /dev/null
+++ b/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb
@@ -0,0 +1,30 @@
+require 'spec_helper'
+
+describe Banzai::ReferenceParser::MergeRequestParser, lib: true do
+  include ReferenceParserHelpers
+
+  let(:user) { create(:user) }
+  let(:merge_request) { create(:merge_request) }
+  subject { described_class.new(merge_request.target_project, user) }
+  let(:link) { empty_html_link }
+
+  describe '#referenced_by' do
+    describe 'when the link has a data-merge-request attribute' do
+      context 'using an existing merge request ID' do
+        it 'returns an Array of merge requests' do
+          link['data-merge-request'] = merge_request.id.to_s
+
+          expect(subject.referenced_by([link])).to eq([merge_request])
+        end
+      end
+
+      context 'using a non-existing merge request ID' do
+        it 'returns an empty Array' do
+          link['data-merge-request'] = ''
+
+          expect(subject.referenced_by([link])).to eq([])
+        end
+      end
+    end
+  end
+end
diff --git a/spec/lib/banzai/reference_parser/milestone_parser_spec.rb b/spec/lib/banzai/reference_parser/milestone_parser_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6aa45a22cc48169841f6b3404c86a76cba1c071b
--- /dev/null
+++ b/spec/lib/banzai/reference_parser/milestone_parser_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe Banzai::ReferenceParser::MilestoneParser, lib: true do
+  include ReferenceParserHelpers
+
+  let(:project) { create(:empty_project, :public) }
+  let(:user) { create(:user) }
+  let(:milestone) { create(:milestone, project: project) }
+  subject { described_class.new(project, user) }
+  let(:link) { empty_html_link }
+
+  describe '#referenced_by' do
+    describe 'when the link has a data-milestone attribute' do
+      context 'using an existing milestone ID' do
+        it 'returns an Array of milestones' do
+          link['data-milestone'] = milestone.id.to_s
+
+          expect(subject.referenced_by([link])).to eq([milestone])
+        end
+      end
+
+      context 'using a non-existing milestone ID' do
+        it 'returns an empty Array' do
+          link['data-milestone'] = ''
+
+          expect(subject.referenced_by([link])).to eq([])
+        end
+      end
+    end
+  end
+end
diff --git a/spec/lib/banzai/reference_parser/snippet_parser_spec.rb b/spec/lib/banzai/reference_parser/snippet_parser_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..59127b7c5d182e2f4dd3d463924f7cf2bda86ad3
--- /dev/null
+++ b/spec/lib/banzai/reference_parser/snippet_parser_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe Banzai::ReferenceParser::SnippetParser, lib: true do
+  include ReferenceParserHelpers
+
+  let(:project) { create(:empty_project, :public) }
+  let(:user) { create(:user) }
+  let(:snippet) { create(:snippet, project: project) }
+  subject { described_class.new(project, user) }
+  let(:link) { empty_html_link }
+
+  describe '#referenced_by' do
+    describe 'when the link has a data-snippet attribute' do
+      context 'using an existing snippet ID' do
+        it 'returns an Array of snippets' do
+          link['data-snippet'] = snippet.id.to_s
+
+          expect(subject.referenced_by([link])).to eq([snippet])
+        end
+      end
+
+      context 'using a non-existing snippet ID' do
+        it 'returns an empty Array' do
+          link['data-snippet'] = ''
+
+          expect(subject.referenced_by([link])).to eq([])
+        end
+      end
+    end
+  end
+end
diff --git a/spec/lib/banzai/reference_parser/user_parser_spec.rb b/spec/lib/banzai/reference_parser/user_parser_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9a82891297d3f2e10b3e6fd8379ff5f1ede383f2
--- /dev/null
+++ b/spec/lib/banzai/reference_parser/user_parser_spec.rb
@@ -0,0 +1,189 @@
+require 'spec_helper'
+
+describe Banzai::ReferenceParser::UserParser, lib: true do
+  include ReferenceParserHelpers
+
+  let(:group) { create(:group) }
+  let(:user) { create(:user) }
+  let(:project) { create(:empty_project, :public, group: group, creator: user) }
+  subject { described_class.new(project, user) }
+  let(:link) { empty_html_link }
+
+  describe '#referenced_by' do
+    context 'when the link has a data-group attribute' do
+      context 'using an existing group ID' do
+        before do
+          link['data-group'] = project.group.id.to_s
+        end
+
+        it 'returns the users of the group' do
+          create(:group_member, group: group, user: user)
+
+          expect(subject.referenced_by([link])).to eq([user])
+        end
+
+        it 'returns an empty Array when the group has no users' do
+          expect(subject.referenced_by([link])).to eq([])
+        end
+      end
+
+      context 'using a non-existing group ID' do
+        it 'returns an empty Array' do
+          link['data-group'] = ''
+
+          expect(subject.referenced_by([link])).to eq([])
+        end
+      end
+    end
+
+    context 'when the link has a data-user attribute' do
+      it 'returns an Array of users' do
+        link['data-user'] = user.id.to_s
+
+        expect(subject.referenced_by([link])).to eq([user])
+      end
+    end
+
+    context 'when the link has a data-project attribute' do
+      context 'using an existing project ID' do
+        let(:contributor) { create(:user) }
+
+        before do
+          project.team << [user, :developer]
+          project.team << [contributor, :developer]
+        end
+
+        it 'returns the members of a project' do
+          link['data-project'] = project.id.to_s
+
+          # This uses an explicit sort to make sure this spec doesn't randomly
+          # fail when objects are returned in a different order.
+          refs = subject.referenced_by([link]).sort_by(&:id)
+
+          expect(refs).to eq([user, contributor])
+        end
+      end
+
+      context 'using a non-existing project ID' do
+        it 'returns an empty Array' do
+          link['data-project'] = ''
+
+          expect(subject.referenced_by([link])).to eq([])
+        end
+      end
+    end
+  end
+
+  describe '#nodes_visible_to_use?' do
+    context 'when the link has a data-group attribute' do
+      context 'using an existing group ID' do
+        before do
+          link['data-group'] = group.id.to_s
+        end
+
+        it 'returns the nodes if the user can read the group' do
+          expect(Ability.abilities).to receive(:allowed?).
+            with(user, :read_group, group).
+            and_return(true)
+
+          expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
+        end
+
+        it 'returns an empty Array if the user can not read the group' do
+          expect(Ability.abilities).to receive(:allowed?).
+            with(user, :read_group, group).
+            and_return(false)
+
+          expect(subject.nodes_visible_to_user(user, [link])).to eq([])
+        end
+      end
+
+      context 'when the link does not have a data-group attribute' do
+        context 'with a data-project attribute' do
+          it 'returns the nodes if the attribute value equals the current project ID' do
+            link['data-project'] = project.id.to_s
+
+            expect(Ability.abilities).not_to receive(:allowed?)
+
+            expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
+          end
+
+          it 'returns the nodes if the user can read the project' do
+            other_project = create(:empty_project, :public)
+
+            link['data-project'] = other_project.id.to_s
+
+            expect(Ability.abilities).to receive(:allowed?).
+              with(user, :read_project, other_project).
+              and_return(true)
+
+            expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
+          end
+
+          it 'returns an empty Array if the user can not read the project' do
+            other_project = create(:empty_project, :public)
+
+            link['data-project'] = other_project.id.to_s
+
+            expect(Ability.abilities).to receive(:allowed?).
+              with(user, :read_project, other_project).
+              and_return(false)
+
+            expect(subject.nodes_visible_to_user(user, [link])).to eq([])
+          end
+        end
+
+        context 'without a data-project attribute' do
+          it 'returns the nodes' do
+            expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
+          end
+        end
+      end
+    end
+  end
+
+  describe '#nodes_user_can_reference' do
+    context 'when the link has a data-author attribute' do
+      it 'returns the nodes when the user is a member of the project' do
+        other_project = create(:project)
+        other_project.team << [user, :developer]
+
+        link['data-project'] = other_project.id.to_s
+        link['data-author'] = user.id.to_s
+
+        expect(subject.nodes_user_can_reference(user, [link])).to eq([link])
+      end
+
+      it 'returns an empty Array when the project could not be found' do
+        link['data-project'] = ''
+        link['data-author'] = user.id.to_s
+
+        expect(subject.nodes_user_can_reference(user, [link])).to eq([])
+      end
+
+      it 'returns an empty Array when the user could not be found' do
+        other_project = create(:project)
+
+        link['data-project'] = other_project.id.to_s
+        link['data-author'] = ''
+
+        expect(subject.nodes_user_can_reference(user, [link])).to eq([])
+      end
+
+      it 'returns an empty Array when the user is not a team member' do
+        other_project = create(:project)
+
+        link['data-project'] = other_project.id.to_s
+        link['data-author'] = user.id.to_s
+
+        expect(subject.nodes_user_can_reference(user, [link])).to eq([])
+      end
+    end
+
+    context 'when the link does not have a data-author attribute' do
+      it 'returns the nodes' do
+        expect(subject.nodes_user_can_reference(user, [link])).to eq([link])
+      end
+    end
+  end
+end
diff --git a/spec/lib/ci/ansi2html_spec.rb b/spec/lib/ci/ansi2html_spec.rb
index 04afbd06929f2c9af4c3f7c0246e473d8cac0290..898f1e84ab0679e229fbee42a1e20e50328290d9 100644
--- a/spec/lib/ci/ansi2html_spec.rb
+++ b/spec/lib/ci/ansi2html_spec.rb
@@ -175,5 +175,14 @@ describe Ci::Ansi2html, lib: true do
 
       it_behaves_like 'stateable converter'
     end
+
+    context 'with new line' do
+      let(:pre_text) { "Hello\r" }
+      let(:pre_html) { "Hello\r" }
+      let(:text) { "\nWorld" }
+      let(:html) { "<br>World" }
+
+      it_behaves_like 'stateable converter'
+    end
   end
 end
diff --git a/spec/lib/ci/charts_spec.rb b/spec/lib/ci/charts_spec.rb
index 50a77308cde81d48d3d2df677dc04964f9cb9c4c..9c6b4ea50861f095b1837bd2797abd656467b67a 100644
--- a/spec/lib/ci/charts_spec.rb
+++ b/spec/lib/ci/charts_spec.rb
@@ -4,13 +4,20 @@ describe Ci::Charts, lib: true do
 
   context "build_times" do
     before do
-      @commit = FactoryGirl.create(:ci_commit)
-      FactoryGirl.create(:ci_build, commit: @commit)
+      @pipeline = FactoryGirl.create(:ci_pipeline)
+      FactoryGirl.create(:ci_build, pipeline: @pipeline)
     end
 
     it 'should return build times in minutes' do
-      chart = Ci::Charts::BuildTime.new(@commit.project)
+      chart = Ci::Charts::BuildTime.new(@pipeline.project)
       expect(chart.build_times).to eq([2])
     end
+
+    it 'should handle nil build times' do
+      create(:ci_pipeline, duration: nil, project: @pipeline.project)
+
+      chart = Ci::Charts::BuildTime.new(@pipeline.project)
+      expect(chart.build_times).to eq([2, 0])
+    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 9eef8ea0976033125dd3938af688fd54107a2a6b..143e2e6d238ea50ed8f7685adb05cb75724fc88b 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -26,7 +26,8 @@ module Ci
           tag_list: [],
           options: {},
           allow_failure: false,
-          when: "on_success"
+          when: "on_success",
+          environment: nil,
         })
       end
 
@@ -387,7 +388,8 @@ module Ci
             services: ["mysql"]
           },
           allow_failure: false,
-          when: "on_success"
+          when: "on_success",
+          environment: nil,
         })
       end
 
@@ -415,7 +417,8 @@ module Ci
             services: ["postgresql"]
           },
           allow_failure: false,
-          when: "on_success"
+          when: "on_success",
+          environment: nil,
         })
       end
     end
@@ -501,6 +504,7 @@ module Ci
                              })
 
           config_processor = GitlabCiYamlProcessor.new(config, path)
+
           builds = config_processor.builds_for_stage_and_ref("test", "master")
           expect(builds.size).to eq(1)
           expect(builds.first[:when]).to eq(when_state)
@@ -572,7 +576,12 @@ module Ci
                              services:      ["mysql"],
                              before_script: ["pwd"],
                              rspec:         {
-                               artifacts: { paths: ["logs/", "binaries/"], untracked: true, name: "custom_name" },
+                               artifacts: {
+                                 paths: ["logs/", "binaries/"],
+                                 untracked: true,
+                                 name: "custom_name",
+                                 expire_in: "7d"
+                               },
                                script: "rspec"
                              }
                            })
@@ -594,13 +603,77 @@ module Ci
             artifacts: {
               name: "custom_name",
               paths: ["logs/", "binaries/"],
-              untracked: true
+              untracked: true,
+              expire_in: "7d"
             }
           },
           when: "on_success",
-          allow_failure: false
+          allow_failure: false,
+          environment: nil,
         })
       end
+
+      %w[on_success on_failure always].each do |when_state|
+        it "returns artifacts for when #{when_state}  defined" do
+          config = YAML.dump({
+                               rspec: {
+                                 script: "rspec",
+                                 artifacts: { paths: ["logs/", "binaries/"], when: when_state }
+                               }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
+
+          builds = config_processor.builds_for_stage_and_ref("test", "master")
+          expect(builds.size).to eq(1)
+          expect(builds.first[:options][:artifacts][:when]).to eq(when_state)
+        end
+      end
+    end
+
+    describe '#environment' do
+      let(:config) do
+        {
+          deploy_to_production: { stage: 'deploy', script: 'test', environment: environment }
+        }
+      end
+
+      let(:processor) { GitlabCiYamlProcessor.new(YAML.dump(config)) }
+      let(:builds) { processor.builds_for_stage_and_ref('deploy', 'master') }
+
+      context 'when a production environment is specified' do
+        let(:environment) { 'production' }
+
+        it 'does return production' do
+          expect(builds.size).to eq(1)
+          expect(builds.first[:environment]).to eq(environment)
+        end
+      end
+
+      context 'when no environment is specified' do
+        let(:environment) { nil }
+
+        it 'does return nil environment' do
+          expect(builds.size).to eq(1)
+          expect(builds.first[:environment]).to be_nil
+        end
+      end
+
+      context 'is not a string' do
+        let(:environment) { 1 }
+
+        it 'raises error' do
+          expect { builds }.to raise_error("deploy_to_production job: environment parameter #{Gitlab::Regex.environment_name_regex_message}")
+        end
+      end
+
+      context 'is not a valid string' do
+        let(:environment) { 'production staging' }
+
+        it 'raises error' do
+          expect { builds }.to raise_error("deploy_to_production job: environment parameter #{Gitlab::Regex.environment_name_regex_message}")
+        end
+      end
     end
 
     describe "Dependencies" do
@@ -619,19 +692,19 @@ module Ci
       context 'no dependencies' do
         let(:dependencies) { }
 
-        it { expect { subject }.to_not raise_error }
+        it { expect { subject }.not_to raise_error }
       end
 
       context 'dependencies to builds' do
         let(:dependencies) { ['build1', 'build2'] }
 
-        it { expect { subject }.to_not raise_error }
+        it { expect { subject }.not_to raise_error }
       end
 
       context 'dependencies to builds defined as symbols' do
         let(:dependencies) { [:build1, :build2] }
 
-        it { expect { subject }.to_not raise_error }
+        it { expect { subject }.not_to raise_error }
       end
 
       context 'undefined dependency' do
@@ -664,7 +737,8 @@ module Ci
             tag_list: [],
             options: {},
             when: "on_success",
-            allow_failure: false
+            allow_failure: false,
+            environment: nil,
           })
         end
       end
@@ -709,7 +783,8 @@ module Ci
             tag_list: [],
             options: {},
             when: "on_success",
-            allow_failure: false
+            allow_failure: false,
+            environment: nil,
           })
           expect(subject.second).to eq({
             except: nil,
@@ -721,7 +796,8 @@ module Ci
             tag_list: [],
             options: {},
             when: "on_success",
-            allow_failure: false
+            allow_failure: false,
+            environment: nil,
           })
         end
       end
@@ -967,6 +1043,27 @@ EOT
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: artifacts:name parameter should be a string")
       end
 
+      it "returns errors if job artifacts:when is not an a predefined value" do
+        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { when: 1 } } })
+        expect do
+          GitlabCiYamlProcessor.new(config)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: artifacts:when parameter should be on_success, on_failure or always")
+      end
+
+      it "returns errors if job artifacts:expire_in is not an a string" do
+        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { expire_in: 1 } } })
+        expect do
+          GitlabCiYamlProcessor.new(config)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: artifacts:expire_in parameter should be a duration")
+      end
+
+      it "returns errors if job artifacts:expire_in is not an a valid duration" do
+        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { expire_in: "7 elephants" } } })
+        expect do
+          GitlabCiYamlProcessor.new(config)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: artifacts:expire_in parameter should be a duration")
+      end
+
       it "returns errors if job artifacts:untracked is not an array of strings" do
         config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { untracked: "string" } } })
         expect do
diff --git a/spec/lib/container_registry/registry_spec.rb b/spec/lib/container_registry/registry_spec.rb
index 2638401ae6e5f6537d0f26d6f24c47947eb40544..4f3f8b24fc43b79e991dc3b64a678c32ac245f0f 100644
--- a/spec/lib/container_registry/registry_spec.rb
+++ b/spec/lib/container_registry/registry_spec.rb
@@ -10,7 +10,7 @@ describe ContainerRegistry::Registry do
   it { is_expected.to respond_to(:uri) }
   it { is_expected.to respond_to(:path) }
 
-  it { expect(subject.repository('test')).to_not be_nil }
+  it { expect(subject.repository('test')).not_to be_nil }
 
   context '#path' do
     subject { registry.path }
diff --git a/spec/lib/container_registry/repository_spec.rb b/spec/lib/container_registry/repository_spec.rb
index e6d66b11e4ebf773e1c423b8b426895417abf5f0..279709521c9743f90d05e66c37dea4516259751e 100644
--- a/spec/lib/container_registry/repository_spec.rb
+++ b/spec/lib/container_registry/repository_spec.rb
@@ -6,7 +6,7 @@ describe ContainerRegistry::Repository do
 
   it { expect(repository).to respond_to(:registry) }
   it { expect(repository).to delegate_method(:client).to(:registry) }
-  it { expect(repository.tag('test')).to_not be_nil }
+  it { expect(repository.tag('test')).not_to be_nil }
 
   context '#path' do
     subject { repository.path }
@@ -27,7 +27,7 @@ describe ContainerRegistry::Repository do
     context '#manifest' do
       subject { repository.manifest }
 
-      it { is_expected.to_not be_nil }
+      it { is_expected.not_to be_nil }
     end
 
     context '#valid?' do
@@ -39,7 +39,7 @@ describe ContainerRegistry::Repository do
     context '#tags' do
       subject { repository.tags }
 
-      it { is_expected.to_not be_empty }
+      it { is_expected.not_to be_empty }
     end
   end
 
diff --git a/spec/lib/container_registry/tag_spec.rb b/spec/lib/container_registry/tag_spec.rb
index 12cf91127edb6e3486344371e6ad561304afeacb..c7324c2bf7726554615a5f800880c966192479c1 100644
--- a/spec/lib/container_registry/tag_spec.rb
+++ b/spec/lib/container_registry/tag_spec.rb
@@ -17,46 +17,85 @@ describe ContainerRegistry::Tag do
   end
 
   context 'manifest processing' do
-    before do
-      stub_request(:get, 'http://example.com/v2/group/test/manifests/tag').
-        with(headers: headers).
-        to_return(
-          status: 200,
-          body: File.read(Rails.root + 'spec/fixtures/container_registry/tag_manifest.json'),
-          headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v2+json' })
-    end
+    context 'schema v1' do
+      before do
+        stub_request(:get, 'http://example.com/v2/group/test/manifests/tag').
+          with(headers: headers).
+          to_return(
+            status: 200,
+            body: File.read(Rails.root + 'spec/fixtures/container_registry/tag_manifest_1.json'),
+            headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v1+prettyjws' })
+      end
 
-    context '#layers' do
-      subject { tag.layers }
+      context '#layers' do
+        subject { tag.layers }
 
-      it { expect(subject.length).to eq(1) }
-    end
+        it { expect(subject.length).to eq(1) }
+      end
+
+      context '#total_size' do
+        subject { tag.total_size }
 
-    context '#total_size' do
-      subject { tag.total_size }
+        it { is_expected.to be_nil }
+      end
 
-      it { is_expected.to eq(2319870) }
+      context 'config processing' do
+        context '#config' do
+          subject { tag.config }
+
+          it { is_expected.to be_nil }
+        end
+
+        context '#created_at' do
+          subject { tag.created_at }
+
+          it { is_expected.to be_nil }
+        end
+      end
     end
 
-    context 'config processing' do
+    context 'schema v2' do
       before do
-        stub_request(:get, 'http://example.com/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac').
-          with(headers: { 'Accept' => 'application/octet-stream' }).
+        stub_request(:get, 'http://example.com/v2/group/test/manifests/tag').
+          with(headers: headers).
           to_return(
             status: 200,
-            body: File.read(Rails.root + 'spec/fixtures/container_registry/config_blob.json'))
+            body: File.read(Rails.root + 'spec/fixtures/container_registry/tag_manifest.json'),
+            headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v2+json' })
       end
 
-      context '#config' do
-        subject { tag.config }
+      context '#layers' do
+        subject { tag.layers }
 
-        it { is_expected.to_not be_nil }
+        it { expect(subject.length).to eq(1) }
       end
 
-      context '#created_at' do
-        subject { tag.created_at }
+      context '#total_size' do
+        subject { tag.total_size }
+
+        it { is_expected.to eq(2319870) }
+      end
+
+      context 'config processing' do
+        before do
+          stub_request(:get, 'http://example.com/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac').
+            with(headers: { 'Accept' => 'application/octet-stream' }).
+            to_return(
+              status: 200,
+              body: File.read(Rails.root + 'spec/fixtures/container_registry/config_blob.json'))
+        end
+
+        context '#config' do
+          subject { tag.config }
+
+          it { is_expected.not_to be_nil }
+        end
+
+        context '#created_at' do
+          subject { tag.created_at }
 
-        it { is_expected.to_not be_nil }
+          it { is_expected.not_to be_nil }
+        end
       end
     end
   end
diff --git a/spec/lib/disable_email_interceptor_spec.rb b/spec/lib/disable_email_interceptor_spec.rb
index c2a7b20b84d90b8f3ec914dd0284a256ae709785..309a88151cf88ca6cf25f49a838124ce7ef75a53 100644
--- a/spec/lib/disable_email_interceptor_spec.rb
+++ b/spec/lib/disable_email_interceptor_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 describe DisableEmailInterceptor, lib: true do
   before do
-    ActionMailer::Base.register_interceptor(DisableEmailInterceptor)
+    Mail.register_interceptor(DisableEmailInterceptor)
   end
 
   it 'should not send emails' do
@@ -14,7 +14,7 @@ describe DisableEmailInterceptor, lib: true do
     # Removing interceptor from the list because unregister_interceptor is
     # implemented in later version of mail gem
     # See: https://github.com/mikel/mail/pull/705
-    Mail.class_variable_set(:@@delivery_interceptors, [])
+    Mail.unregister_interceptor(DisableEmailInterceptor)
   end
 
   def deliver_mail
diff --git a/spec/lib/gitlab/akismet_helper_spec.rb b/spec/lib/gitlab/akismet_helper_spec.rb
index 53f5d6c5c80dd697d024500fe115e3198800cbff..88a71528867c2d2f72296546c4d65837fcf504d7 100644
--- a/spec/lib/gitlab/akismet_helper_spec.rb
+++ b/spec/lib/gitlab/akismet_helper_spec.rb
@@ -6,8 +6,8 @@ describe Gitlab::AkismetHelper, type: :helper do
 
   before do
     allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url))
-    current_application_settings.akismet_enabled = true
-    current_application_settings.akismet_api_key = '12345'
+    allow_any_instance_of(ApplicationSetting).to receive(:akismet_enabled).and_return(true)
+    allow_any_instance_of(ApplicationSetting).to receive(:akismet_api_key).and_return('12345')
   end
 
   describe '#check_for_spam?' do
diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb
index 1fafa03ad0726ea439072e8a06a18ebfc3a12876..aef6b15b2568f94f50890c0a8662e99db15f43a8 100644
--- a/spec/lib/gitlab/auth_spec.rb
+++ b/spec/lib/gitlab/auth_spec.rb
@@ -1,9 +1,47 @@
 require 'spec_helper'
 
 describe Gitlab::Auth, lib: true do
-  let(:gl_auth) { Gitlab::Auth.new }
+  let(:gl_auth) { described_class }
 
-  describe :find do
+  describe 'find_for_git_client' do
+    it 'recognizes CI' do
+      token = '123'
+      project = create(:empty_project)
+      project.update_attributes(runners_token: token, builds_enabled: true)
+      ip = 'ip'
+
+      expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'gitlab-ci-token')
+      expect(gl_auth.find_for_git_client('gitlab-ci-token', token, project: project, ip: ip)).to eq(Gitlab::Auth::Result.new(nil, :ci))
+    end
+
+    it 'recognizes master passwords' do
+      user = create(:user, password: 'password')
+      ip = 'ip'
+
+      expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: user.username)
+      expect(gl_auth.find_for_git_client(user.username, 'password', project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, :gitlab_or_ldap))
+    end
+
+    it 'recognizes OAuth tokens' do
+      user = create(:user)
+      application = Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user)
+      token = Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id)
+      ip = 'ip'
+
+      expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'oauth2')
+      expect(gl_auth.find_for_git_client("oauth2", token.token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, :oauth))
+    end
+
+    it 'returns double nil for invalid credentials' do
+      login = 'foo'
+      ip = 'ip'
+
+      expect(gl_auth).to receive(:rate_limit!).with(ip, success: false, login: login)
+      expect(gl_auth.find_for_git_client(login, 'bar', project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new)
+    end
+  end
+
+  describe 'find_with_user_password' do
     let!(:user) do
       create(:user,
         username: username,
@@ -14,25 +52,25 @@ describe Gitlab::Auth, lib: true do
     let(:password) { 'my-secret' }
 
     it "should find user by valid login/password" do
-      expect( gl_auth.find(username, password) ).to eql user
+      expect( gl_auth.find_with_user_password(username, password) ).to eql user
     end
 
     it 'should find user by valid email/password with case-insensitive email' do
-      expect(gl_auth.find(user.email.upcase, password)).to eql user
+      expect(gl_auth.find_with_user_password(user.email.upcase, password)).to eql user
     end
 
     it 'should find user by valid username/password with case-insensitive username' do
-      expect(gl_auth.find(username.upcase, password)).to eql user
+      expect(gl_auth.find_with_user_password(username.upcase, password)).to eql user
     end
 
     it "should not find user with invalid password" do
       password = 'wrong'
-      expect( gl_auth.find(username, password) ).not_to eql user
+      expect( gl_auth.find_with_user_password(username, password) ).not_to eql user
     end
 
     it "should not find user with invalid login" do
       user = 'wrong'
-      expect( gl_auth.find(username, password) ).not_to eql user
+      expect( gl_auth.find_with_user_password(username, password) ).not_to eql user
     end
 
     context "with kerberos" do
@@ -44,7 +82,7 @@ describe Gitlab::Auth, lib: true do
         allow(Gitlab::Kerberos::Authentication).to receive_messages(valid?: true)
         allow(Gitlab::Kerberos::Authentication).to receive_messages(email: user.email)
 
-        expect( gl_auth.find(username, password) ).to eql user
+        expect( gl_auth.find_with_user_password(username, password) ).to eql user
       end
     end
 
@@ -56,13 +94,13 @@ describe Gitlab::Auth, lib: true do
       it "tries to autheticate with db before ldap" do
         expect(Gitlab::LDAP::Authentication).not_to receive(:login)
 
-        gl_auth.find(username, password)
+        gl_auth.find_with_user_password(username, password)
       end
 
       it "uses ldap as fallback to for authentication" do
         expect(Gitlab::LDAP::Authentication).to receive(:login)
 
-        gl_auth.find('ldap_user', 'password')
+        gl_auth.find_with_user_password('ldap_user', 'password')
       end
     end
   end
diff --git a/spec/lib/gitlab/authority_analyzer_spec.rb b/spec/lib/gitlab/authority_analyzer_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..92d3d86c3c134499f4b8115f57729844c7561891
--- /dev/null
+++ b/spec/lib/gitlab/authority_analyzer_spec.rb
@@ -0,0 +1,61 @@
+require 'spec_helper'
+
+describe Gitlab::AuthorityAnalyzer, lib: true do
+  describe '#calculate' do
+    let(:project) { create(:project) }
+    let(:author) { create(:user) }
+    let(:user_a) { create(:user) }
+    let(:user_b) { create(:user) }
+    let(:merge_request) { create(:merge_request, target_project: project, source_project: project, author: author) }
+    let(:files) { [double(:file, deleted_file: true, old_path: 'foo')] }
+
+    let(:commits) do
+      [
+        double(:commit, author: author),
+        double(:commit, author: user_a),
+        double(:commit, author: user_a),
+        double(:commit, author: user_b),
+        double(:commit, author: author)
+      ]
+    end
+
+    def calculate_approvers(count)
+      described_class.new(merge_request).calculate(count)
+    end
+
+    before do
+      merge_request.compare = double(:compare, diffs: files)
+      allow(merge_request.target_project.repository).to receive(:commits).and_return(commits)
+    end
+
+    context 'when the MR author is in the top contributors' do
+      it 'does not include the MR author' do
+        approvers = calculate_approvers(2)
+
+        expect(approvers).not_to include(author)
+      end
+
+      it 'returns the correct number of contributors' do
+        approvers = calculate_approvers(2)
+
+        expect(approvers.length).to eq(2)
+      end
+    end
+
+    context 'when there are fewer contributors than requested' do
+      it 'returns the full number of users' do
+        approvers = calculate_approvers(5)
+
+        expect(approvers.length).to eq(2)
+      end
+    end
+
+    context 'when there are more contributors than requested' do
+      it 'returns only the top n contributors' do
+        approvers = calculate_approvers(1)
+
+        expect(approvers).to contain_exactly(user_a)
+      end
+    end
+  end
+end
diff --git a/spec/lib/award_emoji_spec.rb b/spec/lib/gitlab/award_emoji_spec.rb
similarity index 70%
rename from spec/lib/award_emoji_spec.rb
rename to spec/lib/gitlab/award_emoji_spec.rb
index 88c22912950610965ab286c8720f7db286b8c009..0f3852b172920d99553d3e8331e826b71d48cbda 100644
--- a/spec/lib/award_emoji_spec.rb
+++ b/spec/lib/gitlab/award_emoji_spec.rb
@@ -1,11 +1,11 @@
 require 'spec_helper'
 
-describe AwardEmoji do
+describe Gitlab::AwardEmoji do
   describe '.urls' do
-    subject { AwardEmoji.urls }
+    subject { Gitlab::AwardEmoji.urls }
 
     it { is_expected.to be_an_instance_of(Array) }
-    it { is_expected.to_not be_empty }
+    it { is_expected.not_to be_empty }
 
     context 'every Hash in the Array' do
       it 'has the correct keys and values' do
@@ -19,7 +19,7 @@ describe AwardEmoji do
 
   describe '.emoji_by_category' do
     it "only contains known categories" do
-      undefined_categories = AwardEmoji.emoji_by_category.keys - AwardEmoji::CATEGORIES.keys
+      undefined_categories = Gitlab::AwardEmoji.emoji_by_category.keys - Gitlab::AwardEmoji::CATEGORIES.keys
       expect(undefined_categories).to be_empty
     end
   end
diff --git a/spec/lib/gitlab/backend/grack_auth_spec.rb b/spec/lib/gitlab/backend/grack_auth_spec.rb
deleted file mode 100644
index 2dc8013590f5a6f57f802c00b8b0189085bce775..0000000000000000000000000000000000000000
--- a/spec/lib/gitlab/backend/grack_auth_spec.rb
+++ /dev/null
@@ -1,284 +0,0 @@
-require "spec_helper"
-
-describe Grack::Auth, lib: true do
-  let(:user)    { create(:user) }
-  let(:project) { create(:project) }
-
-  let(:app)   { lambda { |env| [200, {}, "Success!"] } }
-  let(:env) do
-    {
-      'rack.input'     => '',
-      'REQUEST_METHOD' => 'GET',
-      'QUERY_STRING'   => 'service=git-upload-pack'
-    }
-  end
-  let(:status) { Grack::AuthSpawner::call(env).first }
-
-  describe "#call" do
-    context "when the project doesn't exist" do
-      before do
-        env["PATH_INFO"] = "doesnt/exist.git"
-      end
-
-      context "when no authentication is provided" do
-        it "responds with status 401" do
-          expect(status).to eq(401)
-        end
-      end
-
-      context "when username and password are provided" do
-        context "when authentication fails" do
-          before do
-            env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, "nope")
-          end
-
-          it "responds with status 401" do
-            expect(status).to eq(401)
-          end
-        end
-
-        context "when authentication succeeds" do
-          before do
-            env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
-          end
-
-          it "responds with status 404" do
-            expect(status).to eq(404)
-          end
-        end
-      end
-    end
-
-    context "when the Wiki for a project exists" do
-      before do
-        @wiki = ProjectWiki.new(project)
-        env["PATH_INFO"] = "#{@wiki.repository.path_with_namespace}.git/info/refs"
-        project.update_attribute(:visibility_level, Project::PUBLIC)
-      end
-
-      it "responds with the right project" do
-        response = Grack::AuthSpawner::call(env)
-        json_body = ActiveSupport::JSON.decode(response[2][0])
-
-        expect(response.first).to eq(200)
-        expect(json_body['RepoPath']).to include(@wiki.repository.path_with_namespace)
-      end
-    end
-
-    context "when the project exists" do
-      before do
-        env["PATH_INFO"] = project.path_with_namespace + ".git"
-      end
-
-      context "when the project is public" do
-        before do
-          project.update_attribute(:visibility_level, Project::PUBLIC)
-        end
-
-        it "responds with status 200" do
-          expect(status).to eq(200)
-        end
-      end
-
-      context "when the project is private" do
-        before do
-          project.update_attribute(:visibility_level, Project::PRIVATE)
-        end
-
-        context "when no authentication is provided" do
-          it "responds with status 401" do
-            expect(status).to eq(401)
-          end
-        end
-
-        context "when Kerberos token is provided" do
-          before do
-            allow_any_instance_of(Grack::Auth).to receive(:allow_kerberos_auth?).and_return(true)
-            env["HTTP_AUTHORIZATION"] = "Negotiate #{::Base64.strict_encode64('opaque_request_token')}"
-          end
-
-          shared_examples "RFC4559 compliance" do
-            it "complies with RFC4559" do
-              allow_any_instance_of(Grack::Auth::Request).to receive(:spnego_response_token).and_return("opaque_response_token")
-
-              headers = Grack::AuthSpawner::call(env)[1]
-              expect(headers['WWW-Authenticate'].split("\n")).to include("Negotiate #{::Base64.strict_encode64('opaque_response_token')}")
-            end
-          end
-
-          context "when authentication fails because of invalid Kerberos token" do
-            before do
-              allow_any_instance_of(Grack::Auth::Request).to receive(:spnego_credentials!).and_return(nil)
-            end
-
-            it "responds with status 401" do
-              expect(status).to eq(401)
-            end
-          end
-
-          context "when authentication fails because of unknown Kerberos identity" do
-            before do
-              allow_any_instance_of(Grack::Auth::Request).to receive(:spnego_credentials!).and_return("mylogin@FOO.COM")
-            end
-
-            it "responds with status 401" do
-              expect(status).to eq(401)
-            end
-
-          end
-
-          context "when authentication succeeds" do
-            before do
-              allow_any_instance_of(Grack::Auth::Request).to receive(:spnego_credentials!).and_return("mylogin@FOO.COM")
-              user.identities.build(provider: "kerberos", extern_uid:"mylogin@FOO.COM").save
-            end
-
-            context "when the user has access to the project" do
-              before do
-                project.team << [user, :master]
-              end
-
-              context "when the user is blocked" do
-                before do
-                  user.block
-                  project.team << [user, :master]
-                end
-
-                it "responds with status 404" do
-                  expect(status).to eq(404)
-                end
-              end
-
-              context "when the user isn't blocked" do
-                it "responds with status 200" do
-                  expect(status).to eq(200)
-                end
-              end
-
-              include_examples "RFC4559 compliance"
-            end
-
-            context "when the user doesn't have access to the project" do
-              it "responds with status 404" do
-                expect(status).to eq(404)
-              end
-
-              include_examples "RFC4559 compliance"
-            end
-          end
-        end
-
-        context "when username and password are provided" do
-          context "when authentication fails" do
-            before do
-              env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, "nope")
-            end
-
-            it "responds with status 401" do
-              expect(status).to eq(401)
-            end
-
-            context "when the user is IP banned" do
-              before do
-                expect(Rack::Attack::Allow2Ban).to receive(:filter).and_return(true)
-                allow_any_instance_of(Rack::Request).to receive(:ip).and_return('1.2.3.4')
-              end
-
-              it "responds with status 401" do
-                expect(status).to eq(401)
-              end
-            end
-          end
-
-          context "when authentication succeeds" do
-            before do
-              env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
-            end
-
-            context "when the user has access to the project" do
-              before do
-                project.team << [user, :master]
-              end
-
-              context "when the user is blocked" do
-                before do
-                  user.block
-                  project.team << [user, :master]
-                end
-
-                it "responds with status 404" do
-                  expect(status).to eq(404)
-                end
-              end
-
-              context "when the user isn't blocked" do
-                before do
-                  expect(Rack::Attack::Allow2Ban).to receive(:reset)
-                end
-
-                it "responds with status 200" do
-                  expect(status).to eq(200)
-                end
-              end
-
-              context "when blank password attempts follow a valid login" do
-                let(:options) { Gitlab.config.rack_attack.git_basic_auth }
-                let(:maxretry) { options[:maxretry] - 1 }
-                let(:ip) { '1.2.3.4' }
-
-                before do
-                  allow_any_instance_of(Rack::Request).to receive(:ip).and_return(ip)
-                  Rack::Attack::Allow2Ban.reset(ip, options)
-                end
-
-                after do
-                  Rack::Attack::Allow2Ban.reset(ip, options)
-                end
-
-                def attempt_login(include_password)
-                  password = include_password ? user.password : ""
-                  env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, password)
-                  Grack::AuthSpawner::call(env).first
-                end
-
-                it "repeated attempts followed by successful attempt" do
-                  maxretry.times.each do
-                    expect(attempt_login(false)).to eq(401)
-                  end
-
-                  expect(attempt_login(true)).to eq(200)
-                  expect(Rack::Attack::Allow2Ban.banned?(ip)).to be_falsey
-
-                  maxretry.times.each do
-                    expect(attempt_login(false)).to eq(401)
-                  end
-                end
-              end
-            end
-
-            context "when the user doesn't have access to the project" do
-              it "responds with status 404" do
-                expect(status).to eq(404)
-              end
-            end
-          end
-        end
-
-        context "when a gitlab ci token is provided" do
-          let(:token) { "123" }
-          let(:project) { FactoryGirl.create :empty_project }
-
-          before do
-            project.update_attributes(runners_token: token, builds_enabled: true)
-
-            env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials("gitlab-ci-token", token)
-          end
-
-          it "responds with status 200" do
-            expect(status).to eq(200)
-          end
-        end
-      end
-    end
-  end
-end
diff --git a/spec/lib/gitlab/badge/build_spec.rb b/spec/lib/gitlab/badge/build_spec.rb
index b6f7a2e7ec4b44c0711e3c898d520a06a69e32af..2034445a197320280ae1c625988b25c6188e104b 100644
--- a/spec/lib/gitlab/badge/build_spec.rb
+++ b/spec/lib/gitlab/badge/build_spec.rb
@@ -42,9 +42,7 @@ describe Gitlab::Badge::Build do
   end
 
   context 'build exists' do
-    let(:ci_commit) { create(:ci_commit, project: project, sha: sha, ref: branch) }
-    let!(:build) { create(:ci_build, commit: ci_commit) }
-
+    let!(:build) { create_build(project, sha, branch) }
 
     context 'build success' do
       before { build.success! }
@@ -96,6 +94,28 @@ describe Gitlab::Badge::Build do
     end
   end
 
+  context 'when outdated pipeline for given ref exists' do
+    before do
+      build = create_build(project, sha, branch)
+      build.success!
+
+      old_build = create_build(project, '11eeffdd', branch)
+      old_build.drop!
+    end
+
+    it 'does not take outdated pipeline into account' do
+      expect(badge.to_s).to eq 'build-success'
+    end
+  end
+
+  def create_build(project, sha, branch)
+    pipeline = create(:ci_pipeline, project: project,
+                                    sha: sha,
+                                    ref: branch)
+
+    create(:ci_build, pipeline: pipeline)
+  end
+
   def status_node(data, status)
     xml = Nokogiri::XML.parse(data)
     xml.at(%Q{text:contains("#{status}")})
diff --git a/spec/lib/gitlab/bitbucket_import/client_spec.rb b/spec/lib/gitlab/bitbucket_import/client_spec.rb
index af839f4242155a036fbdbfd45379cbba074f92b6..760d66a148883cfe786dc3b8732078da9ee6d410 100644
--- a/spec/lib/gitlab/bitbucket_import/client_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/client_spec.rb
@@ -1,12 +1,14 @@
 require 'spec_helper'
 
 describe Gitlab::BitbucketImport::Client, lib: true do
+  include ImportSpecHelper
+
   let(:token) { '123456' }
   let(:secret) { 'secret' }
   let(:client) { Gitlab::BitbucketImport::Client.new(token, secret) }
 
   before do
-    Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "bitbucket")
+    stub_omniauth_provider('bitbucket')
   end
 
   it 'all OAuth client options are symbols' do
@@ -59,7 +61,7 @@ describe Gitlab::BitbucketImport::Client, lib: true do
                                                              bitbucket_access_token_secret: "test" } })
       project.import_url = "ssh://git@bitbucket.org/test/test.git"
 
-      expect { described_class.from_project(project) }.to_not raise_error
+      expect { described_class.from_project(project) }.not_to raise_error
     end
   end
 end
diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
index 1a833f255a56a7ee20b29d7fbf20544c878000ac..aa00f32becbec1861e12a5cc6db056add55a804b 100644
--- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
@@ -1,8 +1,10 @@
 require 'spec_helper'
 
 describe Gitlab::BitbucketImport::Importer, lib: true do
+  include ImportSpecHelper
+
   before do
-    Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "bitbucket")
+    stub_omniauth_provider('bitbucket')
   end
 
   let(:statuses) do
diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb
index 46a5b7fce6537f5e9ce4266ea5316349ab2f7d90..711a3e1c7d41a4f827435d2ff165bba0e0978b15 100644
--- a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb
@@ -122,7 +122,7 @@ describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do
 
   describe 'empty path', path: '' do
     subject { |example| path(example) }
-    it { is_expected.to_not have_parent }
+    it { is_expected.not_to have_parent }
 
     describe '#children' do
       subject { |example| path(example).children }
diff --git a/spec/lib/gitlab/ci/config/loader_spec.rb b/spec/lib/gitlab/ci/config/loader_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2d44b1f60f1607ba9a61b76be682daf834102a6f
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/loader_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::Loader do
+  let(:loader) { described_class.new(yml) }
+
+  context 'when yaml syntax is correct' do
+    let(:yml) { 'image: ruby:2.2' }
+
+    describe '#valid?' do
+      it 'returns true' do
+        expect(loader.valid?).to be true
+      end
+    end
+
+    describe '#load!' do
+      it 'returns a valid hash' do
+        expect(loader.load!).to eq(image: 'ruby:2.2')
+      end
+    end
+  end
+
+  context 'when yaml syntax is incorrect' do
+    let(:yml) { '// incorrect' }
+
+    describe '#valid?' do
+      it 'returns false' do
+        expect(loader.valid?).to be false
+      end
+    end
+
+    describe '#load!' do
+      it 'raises error' do
+        expect { loader.load! }.to raise_error(
+          Gitlab::Ci::Config::Loader::FormatError,
+          'Invalid configuration format'
+        )
+      end
+    end
+  end
+
+  context 'when yaml config is empty' do
+    let(:yml) { '' }
+
+    describe '#valid?' do
+      it 'returns false' do
+        expect(loader.valid?).to be false
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/ci/config/node/configurable_spec.rb b/spec/lib/gitlab/ci/config/node/configurable_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..47c68f96dc8ab603743f67ee38aa0e0b8dff4742
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/node/configurable_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::Node::Configurable do
+  let(:node) { Class.new }
+
+  before do
+    node.include(described_class)
+  end
+
+  describe 'allowed nodes' do
+    before do
+      node.class_eval do
+        allow_node :object, Object, description: 'test object'
+      end
+    end
+
+    describe '#allowed_nodes' do
+      it 'has valid allowed nodes' do
+        expect(node.allowed_nodes).to include :object
+      end
+
+      it 'creates a node factory' do
+        expect(node.allowed_nodes[:object])
+          .to be_an_instance_of Gitlab::Ci::Config::Node::Factory
+      end
+
+      it 'returns a duplicated factory object' do
+        first_factory = node.allowed_nodes[:object]
+        second_factory = node.allowed_nodes[:object]
+
+        expect(first_factory).not_to be_equal(second_factory)
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/ci/config/node/factory_spec.rb b/spec/lib/gitlab/ci/config/node/factory_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d681aa32456fbf43a1f8ebb84876525260a8cc1d
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/node/factory_spec.rb
@@ -0,0 +1,49 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::Node::Factory do
+  describe '#create!' do
+    let(:factory) { described_class.new(entry_class) }
+    let(:entry_class) { Gitlab::Ci::Config::Node::Script }
+
+    context 'when value setting value' do
+      it 'creates entry with valid value' do
+        entry = factory
+          .with(value: ['ls', 'pwd'])
+          .create!
+
+        expect(entry.value).to eq "ls\npwd"
+      end
+
+      context 'when setting description' do
+        it 'creates entry with description' do
+          entry = factory
+            .with(value: ['ls', 'pwd'])
+            .with(description: 'test description')
+            .create!
+
+          expect(entry.value).to eq "ls\npwd"
+          expect(entry.description).to eq 'test description'
+        end
+      end
+    end
+
+    context 'when not setting value' do
+      it 'raises error' do
+        expect { factory.create! }.to raise_error(
+          Gitlab::Ci::Config::Node::Factory::InvalidFactory
+        )
+      end
+    end
+
+    context 'when creating a null entry' do
+      it 'creates a null entry' do
+        entry = factory
+          .with(value: nil)
+          .nullify!
+          .create!
+
+        expect(entry).to be_an_instance_of Gitlab::Ci::Config::Node::Null
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/ci/config/node/global_spec.rb b/spec/lib/gitlab/ci/config/node/global_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b1972172435a29f0950d665f63138860438a2b7b
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/node/global_spec.rb
@@ -0,0 +1,104 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::Node::Global do
+  let(:global) { described_class.new(hash) }
+
+  describe '#allowed_nodes' do
+    it 'can contain global config keys' do
+      expect(global.allowed_nodes).to include :before_script
+    end
+
+    it 'returns a hash' do
+      expect(global.allowed_nodes).to be_a Hash
+    end
+  end
+
+  context 'when hash is valid' do
+    let(:hash) do
+      { before_script: ['ls', 'pwd'] }
+    end
+
+    describe '#process!' do
+      before { global.process! }
+
+      it 'creates nodes hash' do
+        expect(global.nodes).to be_an Array
+      end
+
+      it 'creates node object for each entry' do
+        expect(global.nodes.count).to eq 1
+      end
+
+      it 'creates node object using valid class' do
+        expect(global.nodes.first)
+          .to be_an_instance_of Gitlab::Ci::Config::Node::Script
+      end
+
+      it 'sets correct description for nodes' do
+        expect(global.nodes.first.description)
+          .to eq 'Script that will be executed before each job.'
+      end
+    end
+
+    describe '#leaf?' do
+      it 'is not leaf' do
+        expect(global).not_to be_leaf
+      end
+    end
+
+    describe '#before_script' do
+      context 'when processed' do
+        before { global.process! }
+
+        it 'returns correct script' do
+          expect(global.before_script).to eq "ls\npwd"
+        end
+      end
+
+      context 'when not processed' do
+        it 'returns nil' do
+          expect(global.before_script).to be nil
+        end
+      end
+    end
+  end
+
+  context 'when hash is not valid' do
+    before { global.process! }
+
+    let(:hash) do
+      { before_script: 'ls' }
+    end
+
+    describe '#valid?' do
+      it 'is not valid' do
+        expect(global).not_to be_valid
+      end
+    end
+
+    describe '#errors' do
+      it 'reports errors from child nodes' do
+        expect(global.errors)
+          .to include 'before_script should be an array of strings'
+      end
+    end
+
+    describe '#before_script' do
+      it 'raises error' do
+        expect { global.before_script }.to raise_error(
+          Gitlab::Ci::Config::Node::Entry::InvalidError
+        )
+      end
+    end
+  end
+
+  context 'when value is not a hash' do
+    let(:hash) { [] }
+
+    describe '#valid?' do
+      it 'is not valid' do
+        expect(global).not_to be_valid
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/ci/config/node/null_spec.rb b/spec/lib/gitlab/ci/config/node/null_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..36101c624624fb873799513e93104b2665b00dfc
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/node/null_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::Node::Null do
+  let(:entry) { described_class.new(nil) }
+
+  describe '#leaf?' do
+    it 'is leaf node' do
+      expect(entry).to be_leaf
+    end
+  end
+
+  describe '#any_method' do
+    it 'responds with nil' do
+      expect(entry.any_method).to be nil
+    end
+  end
+
+  describe '#value' do
+    it 'returns nil' do
+      expect(entry.value).to be nil
+    end
+  end
+end
diff --git a/spec/lib/gitlab/ci/config/node/script_spec.rb b/spec/lib/gitlab/ci/config/node/script_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e4d6481f8a5711bacfe6705aa082d50d7b369c1d
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/node/script_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::Node::Script do
+  let(:entry) { described_class.new(value) }
+
+  describe '#validate!' do
+    before { entry.validate! }
+
+    context 'when entry value is correct' do
+      let(:value) { ['ls', 'pwd'] }
+
+      describe '#value' do
+        it 'returns concatenated command' do
+          expect(entry.value).to eq "ls\npwd"
+        end
+      end
+
+      describe '#errors' do
+        it 'does not append errors' do
+          expect(entry.errors).to be_empty
+        end
+      end
+
+      describe '#valid?' do
+        it 'is valid' do
+          expect(entry).to be_valid
+        end
+      end
+    end
+
+    context 'when entry value is not correct' do
+      let(:value) { 'ls' }
+
+      describe '#errors' do
+        it 'saves errors' do
+          expect(entry.errors)
+            .to include /should be an array of strings/
+        end
+      end
+
+      describe '#valid?' do
+        it 'is not valid' do
+          expect(entry).not_to be_valid
+        end
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3871d939feb62c16ebfaef7d32c06aef82524a2a
--- /dev/null
+++ b/spec/lib/gitlab/ci/config_spec.rb
@@ -0,0 +1,73 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Config do
+  let(:config) do
+    described_class.new(yml)
+  end
+
+  context 'when config is valid' do
+    let(:yml) do
+      <<-EOS
+        image: ruby:2.2
+
+        rspec:
+          script:
+            - gem install rspec
+            - rspec
+      EOS
+    end
+
+    describe '#to_hash' do
+      it 'returns hash created from string' do
+        hash = {
+          image: 'ruby:2.2',
+          rspec: {
+            script: ['gem install rspec',
+                     'rspec']
+          }
+        }
+
+        expect(config.to_hash).to eq hash
+      end
+
+      describe '#valid?' do
+        it 'is valid' do
+          expect(config).to be_valid
+        end
+
+        it 'has no errors' do
+          expect(config.errors).to be_empty
+        end
+      end
+    end
+
+    context 'when config is invalid' do
+      context 'when yml is incorrect' do
+        let(:yml) { '// invalid' }
+
+        describe '.new' do
+          it 'raises error' do
+            expect { config }.to raise_error(
+              Gitlab::Ci::Config::Loader::FormatError,
+              /Invalid configuration format/
+            )
+          end
+        end
+      end
+
+      context 'when config logic is incorrect' do
+        let(:yml) { 'before_script: "ls"' }
+
+        describe '#valid?' do
+          it 'is not valid' do
+            expect(config).not_to be_valid
+          end
+
+          it 'has errors' do
+            expect(config.errors).not_to be_empty
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 35ade7a2be052c50554aa8d607320fc6cfc06276..1ec539066a76ee1d9a53188d1ef0288288aa2cf7 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -16,14 +16,21 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
       end
 
       context 'using PostgreSQL' do
-        it 'creates the index concurrently' do
-          expect(Gitlab::Database).to receive(:postgresql?).and_return(true)
+        before { expect(Gitlab::Database).to receive(:postgresql?).and_return(true) }
 
+        it 'creates the index concurrently' do
           expect(model).to receive(:add_index).
             with(:users, :foo, algorithm: :concurrently)
 
           model.add_concurrent_index(:users, :foo)
         end
+
+        it 'creates unique index concurrently' do
+          expect(model).to receive(:add_index).
+            with(:users, :foo, { algorithm: :concurrently, unique: true })
+
+          model.add_concurrent_index(:users, :foo, unique: true)
+        end
       end
 
       context 'using MySQL' do
@@ -31,7 +38,7 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
           expect(Gitlab::Database).to receive(:postgresql?).and_return(false)
 
           expect(model).to receive(:add_index).
-            with(:users, :foo)
+            with(:users, :foo, {})
 
           model.add_concurrent_index(:users, :foo)
         end
@@ -113,6 +120,19 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
           model.add_column_with_default(:projects, :foo, :integer, default: 10)
         end.to raise_error(RuntimeError)
       end
+
+      it 'removes the added column whenever changing a column NULL constraint fails' do
+        expect(model).to receive(:change_column_null).
+          with(:projects, :foo, false).
+          and_raise(RuntimeError)
+
+        expect(model).to receive(:remove_column).
+          with(:projects, :foo)
+
+        expect do
+          model.add_column_with_default(:projects, :foo, :integer, default: 10)
+        end.to raise_error(RuntimeError)
+      end
     end
 
     context 'inside a transaction' do
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index d0a447753b719790371268c3a02841335cf4fbbd..3031559c61398463ff3b0553484b77b7307df641 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -39,6 +39,22 @@ describe Gitlab::Database, lib: true do
     end
   end
 
+  describe '.nulls_last_order' do
+    context 'when using PostgreSQL' do
+      before { expect(described_class).to receive(:postgresql?).and_return(true) }
+
+      it { expect(described_class.nulls_last_order('column', 'ASC')).to eq 'column ASC NULLS LAST'}
+      it { expect(described_class.nulls_last_order('column', 'DESC')).to eq 'column DESC NULLS LAST'}
+    end
+
+    context 'when using MySQL' do
+      before { expect(described_class).to receive(:postgresql?).and_return(false) }
+
+      it { expect(described_class.nulls_last_order('column', 'ASC')).to eq 'column IS NULL, column ASC'}
+      it { expect(described_class.nulls_last_order('column', 'DESC')).to eq 'column DESC'}
+    end
+  end
+
   describe '#true_value' do
     it 'returns correct value for PostgreSQL' do
       expect(described_class).to receive(:postgresql?).and_return(true)
diff --git a/spec/lib/gitlab/elastic/indexer_spec.rb b/spec/lib/gitlab/elastic/indexer_spec.rb
index ad8b45d2f16fc3903ba26f395da46c239516f7b5..359bb2b957422c8ba8eaa7ece5f416168c955933 100644
--- a/spec/lib/gitlab/elastic/indexer_spec.rb
+++ b/spec/lib/gitlab/elastic/indexer_spec.rb
@@ -2,13 +2,14 @@ require 'spec_helper'
 
 describe "Indexer" do
   it "runs commands" do
+    stub_application_setting(es_host: ['elastic-host1', 'elastic-host2'])
     expect(Gitlab::Popen).to receive(:popen).with(
       [File.join(Rails.root, 'bin/elastic_repo_indexer'), '1', 'full_repo_path'],
       nil,
       hash_including(
         'ELASTIC_CONNECTION_INFO' => {
-                                       host: Gitlab.config.elasticsearch.host,
-                                       port: Gitlab.config.elasticsearch.port
+                                       host: ApplicationSetting.current.elasticsearch_host,
+                                       port: ApplicationSetting.current.elasticsearch_port
                                      }.to_json,
         'RAILS_ENV'               => Rails.env,
         'FROM_SHA' => '000000',
diff --git a/spec/lib/gitlab/elastic/project_search_results_spec.rb b/spec/lib/gitlab/elastic/project_search_results_spec.rb
index cee317fe21377ca07eecd90accff383244896763..cedc3b66f7ed1301963515d1bdebd4113917f19b 100644
--- a/spec/lib/gitlab/elastic/project_search_results_spec.rb
+++ b/spec/lib/gitlab/elastic/project_search_results_spec.rb
@@ -6,13 +6,13 @@ describe Gitlab::Elastic::ProjectSearchResults, lib: true do
   let(:query) { 'hello world' }
 
   before do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(true)
+    stub_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
     Project.__elasticsearch__.create_index!
     Issue.__elasticsearch__.create_index!
   end
 
   after do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(false)
+    stub_application_setting(elasticsearch_search: false, elasticsearch_indexing: false)
     Project.__elasticsearch__.delete_index!
     Issue.__elasticsearch__.delete_index!
   end
@@ -122,6 +122,18 @@ describe Gitlab::Elastic::ProjectSearchResults, lib: true do
       expect(results.issues_count).to eq 3
     end
 
+    it 'should not list project confidential issues for project members with guest role' do
+      project.team << [member, :guest]
+
+      results = described_class.new(member, project.id, query)
+      issues = results.objects('issues')
+
+      expect(issues).to include issue
+      expect(issues).not_to include security_issue_1
+      expect(issues).not_to include security_issue_2
+      expect(results.issues_count).to eq 1
+    end
+
     it 'should list all project issues for admin' do
       results = described_class.new(admin, project.id, query)
       issues = results.objects('issues')
diff --git a/spec/lib/gitlab/elastic/search_results_spec.rb b/spec/lib/gitlab/elastic/search_results_spec.rb
index 667a466dfdb0c7394d3e4a104618fd8168ce9239..1102f24e4ba1d581d2b925af11dff29e0868125f 100644
--- a/spec/lib/gitlab/elastic/search_results_spec.rb
+++ b/spec/lib/gitlab/elastic/search_results_spec.rb
@@ -2,11 +2,11 @@ require 'spec_helper'
 
 describe Gitlab::Elastic::SearchResults, lib: true do
   before do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(true)
+    stub_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
   end
 
   after do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(false)
+    stub_application_setting(elasticsearch_search: false, elasticsearch_indexing: false)
   end
 
   let(:user) { create(:user) }
@@ -89,14 +89,17 @@ describe Gitlab::Elastic::SearchResults, lib: true do
     let(:non_member) { create(:user) }
     let(:member) { create(:user) }
     let(:admin) { create(:admin) }
-    let!(:issue) { create(:issue, project: project_1, title: 'Issue 1', iid: 1) }
-    let!(:security_issue_1) { create(:issue, :confidential, project: project_1, title: 'Security issue 1', author: author, iid: 2) }
-    let!(:security_issue_2) { create(:issue, :confidential, title: 'Security issue 2', project: project_1, assignee: assignee, iid: 3) }
-    let!(:security_issue_3) { create(:issue, :confidential, project: project_2, title: 'Security issue 3', author: author, iid: 1) }
-    let!(:security_issue_4) { create(:issue, :confidential, project: project_3, title: 'Security issue 4', assignee: assignee, iid: 1) }
-    let!(:security_issue_5) { create(:issue, :confidential, project: project_4, title: 'Security issue 5', iid: 1) }
 
     before do
+      Issue.__elasticsearch__.create_index!
+
+      @issue = create(:issue, project: project_1, title: 'Issue 1', iid: 1)
+      @security_issue_1 = create(:issue, :confidential, project: project_1, title: 'Security issue 1', author: author, iid: 2)
+      @security_issue_2 = create(:issue, :confidential, title: 'Security issue 2', project: project_1, assignee: assignee, iid: 3)
+      @security_issue_3 = create(:issue, :confidential, project: project_2, title: 'Security issue 3', author: author, iid: 1)
+      @security_issue_4 = create(:issue, :confidential, project: project_3, title: 'Security issue 4', assignee: assignee, iid: 1)
+      @security_issue_5 = create(:issue, :confidential, project: project_4, title: 'Security issue 5', iid: 1)
+
       Issue.__elasticsearch__.refresh_index!
     end
 
@@ -107,12 +110,12 @@ describe Gitlab::Elastic::SearchResults, lib: true do
         results = described_class.new(nil, limit_project_ids, query)
         issues = results.objects('issues')
 
-        expect(issues).to include issue
-        expect(issues).not_to include security_issue_1
-        expect(issues).not_to include security_issue_2
-        expect(issues).not_to include security_issue_3
-        expect(issues).not_to include security_issue_4
-        expect(issues).not_to include security_issue_5
+        expect(issues).to include @issue
+        expect(issues).not_to include @security_issue_1
+        expect(issues).not_to include @security_issue_2
+        expect(issues).not_to include @security_issue_3
+        expect(issues).not_to include @security_issue_4
+        expect(issues).not_to include @security_issue_5
         expect(results.issues_count).to eq 1
       end
 
@@ -120,12 +123,12 @@ describe Gitlab::Elastic::SearchResults, lib: true do
         results = described_class.new(non_member, limit_project_ids, query)
         issues = results.objects('issues')
 
-        expect(issues).to include issue
-        expect(issues).not_to include security_issue_1
-        expect(issues).not_to include security_issue_2
-        expect(issues).not_to include security_issue_3
-        expect(issues).not_to include security_issue_4
-        expect(issues).not_to include security_issue_5
+        expect(issues).to include @issue
+        expect(issues).not_to include @security_issue_1
+        expect(issues).not_to include @security_issue_2
+        expect(issues).not_to include @security_issue_3
+        expect(issues).not_to include @security_issue_4
+        expect(issues).not_to include @security_issue_5
         expect(results.issues_count).to eq 1
       end
 
@@ -133,12 +136,12 @@ describe Gitlab::Elastic::SearchResults, lib: true do
         results = described_class.new(author, limit_project_ids, query)
         issues = results.objects('issues')
 
-        expect(issues).to include issue
-        expect(issues).to include security_issue_1
-        expect(issues).not_to include security_issue_2
-        expect(issues).to include security_issue_3
-        expect(issues).not_to include security_issue_4
-        expect(issues).not_to include security_issue_5
+        expect(issues).to include @issue
+        expect(issues).to include @security_issue_1
+        expect(issues).not_to include @security_issue_2
+        expect(issues).to include @security_issue_3
+        expect(issues).not_to include @security_issue_4
+        expect(issues).not_to include @security_issue_5
         expect(results.issues_count).to eq 3
       end
 
@@ -146,12 +149,12 @@ describe Gitlab::Elastic::SearchResults, lib: true do
         results = described_class.new(assignee, limit_project_ids, query)
         issues = results.objects('issues')
 
-        expect(issues).to include issue
-        expect(issues).not_to include security_issue_1
-        expect(issues).to include security_issue_2
-        expect(issues).not_to include security_issue_3
-        expect(issues).to include security_issue_4
-        expect(issues).not_to include security_issue_5
+        expect(issues).to include @issue
+        expect(issues).not_to include @security_issue_1
+        expect(issues).to include @security_issue_2
+        expect(issues).not_to include @security_issue_3
+        expect(issues).to include @security_issue_4
+        expect(issues).not_to include @security_issue_5
         expect(results.issues_count).to eq 3
       end
 
@@ -162,12 +165,12 @@ describe Gitlab::Elastic::SearchResults, lib: true do
         results = described_class.new(member, limit_project_ids, query)
         issues = results.objects('issues')
 
-        expect(issues).to include issue
-        expect(issues).to include security_issue_1
-        expect(issues).to include security_issue_2
-        expect(issues).to include security_issue_3
-        expect(issues).not_to include security_issue_4
-        expect(issues).not_to include security_issue_5
+        expect(issues).to include @issue
+        expect(issues).to include @security_issue_1
+        expect(issues).to include @security_issue_2
+        expect(issues).to include @security_issue_3
+        expect(issues).not_to include @security_issue_4
+        expect(issues).not_to include @security_issue_5
         expect(results.issues_count).to eq 4
       end
 
@@ -175,12 +178,12 @@ describe Gitlab::Elastic::SearchResults, lib: true do
         results = described_class.new(admin, limit_project_ids, query)
         issues = results.objects('issues')
 
-        expect(issues).to include issue
-        expect(issues).to include security_issue_1
-        expect(issues).to include security_issue_2
-        expect(issues).to include security_issue_3
-        expect(issues).to include security_issue_4
-        expect(issues).not_to include security_issue_5
+        expect(issues).to include @issue
+        expect(issues).to include @security_issue_1
+        expect(issues).to include @security_issue_2
+        expect(issues).to include @security_issue_3
+        expect(issues).to include @security_issue_4
+        expect(issues).not_to include @security_issue_5
         expect(results.issues_count).to eq 5
       end
     end
@@ -192,12 +195,12 @@ describe Gitlab::Elastic::SearchResults, lib: true do
         results = described_class.new(nil, limit_project_ids, query)
         issues = results.objects('issues')
 
-        expect(issues).to include issue
-        expect(issues).not_to include security_issue_1
-        expect(issues).not_to include security_issue_2
-        expect(issues).not_to include security_issue_3
-        expect(issues).not_to include security_issue_4
-        expect(issues).not_to include security_issue_5
+        expect(issues).to include @issue
+        expect(issues).not_to include @security_issue_1
+        expect(issues).not_to include @security_issue_2
+        expect(issues).not_to include @security_issue_3
+        expect(issues).not_to include @security_issue_4
+        expect(issues).not_to include @security_issue_5
         expect(results.issues_count).to eq 1
       end
 
@@ -205,12 +208,12 @@ describe Gitlab::Elastic::SearchResults, lib: true do
         results = described_class.new(non_member, limit_project_ids, query)
         issues = results.objects('issues')
 
-        expect(issues).to include issue
-        expect(issues).not_to include security_issue_1
-        expect(issues).not_to include security_issue_2
-        expect(issues).not_to include security_issue_3
-        expect(issues).not_to include security_issue_4
-        expect(issues).not_to include security_issue_5
+        expect(issues).to include @issue
+        expect(issues).not_to include @security_issue_1
+        expect(issues).not_to include @security_issue_2
+        expect(issues).not_to include @security_issue_3
+        expect(issues).not_to include @security_issue_4
+        expect(issues).not_to include @security_issue_5
         expect(results.issues_count).to eq 1
       end
 
@@ -218,12 +221,12 @@ describe Gitlab::Elastic::SearchResults, lib: true do
         results = described_class.new(author, limit_project_ids, query)
         issues = results.objects('issues')
 
-        expect(issues).to include issue
-        expect(issues).not_to include security_issue_1
-        expect(issues).not_to include security_issue_2
-        expect(issues).to include security_issue_3
-        expect(issues).not_to include security_issue_4
-        expect(issues).not_to include security_issue_5
+        expect(issues).to include @issue
+        expect(issues).not_to include @security_issue_1
+        expect(issues).not_to include @security_issue_2
+        expect(issues).to include @security_issue_3
+        expect(issues).not_to include @security_issue_4
+        expect(issues).not_to include @security_issue_5
         expect(results.issues_count).to eq 2
       end
 
@@ -231,12 +234,12 @@ describe Gitlab::Elastic::SearchResults, lib: true do
         results = described_class.new(assignee, limit_project_ids, query)
         issues = results.objects('issues')
 
-        expect(issues).to include issue
-        expect(issues).not_to include security_issue_1
-        expect(issues).not_to include security_issue_2
-        expect(issues).not_to include security_issue_3
-        expect(issues).to include security_issue_4
-        expect(issues).not_to include security_issue_5
+        expect(issues).to include @issue
+        expect(issues).not_to include @security_issue_1
+        expect(issues).not_to include @security_issue_2
+        expect(issues).not_to include @security_issue_3
+        expect(issues).to include @security_issue_4
+        expect(issues).not_to include @security_issue_5
         expect(results.issues_count).to eq 2
       end
 
@@ -247,12 +250,12 @@ describe Gitlab::Elastic::SearchResults, lib: true do
         results = described_class.new(member, limit_project_ids, query)
         issues = results.objects('issues')
 
-        expect(issues).to include issue
-        expect(issues).not_to include security_issue_1
-        expect(issues).not_to include security_issue_2
-        expect(issues).to include security_issue_3
-        expect(issues).to include security_issue_4
-        expect(issues).not_to include security_issue_5
+        expect(issues).to include @issue
+        expect(issues).not_to include @security_issue_1
+        expect(issues).not_to include @security_issue_2
+        expect(issues).to include @security_issue_3
+        expect(issues).to include @security_issue_4
+        expect(issues).not_to include @security_issue_5
         expect(results.issues_count).to eq 3
       end
 
@@ -260,12 +263,12 @@ describe Gitlab::Elastic::SearchResults, lib: true do
         results = described_class.new(admin, limit_project_ids, query)
         issues = results.objects('issues')
 
-        expect(issues).to include issue
-        expect(issues).not_to include security_issue_1
-        expect(issues).not_to include security_issue_2
-        expect(issues).to include security_issue_3
-        expect(issues).to include security_issue_4
-        expect(issues).not_to include security_issue_5
+        expect(issues).to include @issue
+        expect(issues).not_to include @security_issue_1
+        expect(issues).not_to include @security_issue_2
+        expect(issues).to include @security_issue_3
+        expect(issues).to include @security_issue_4
+        expect(issues).not_to include @security_issue_5
         expect(results.issues_count).to eq 3
       end
     end
diff --git a/spec/lib/gitlab/geo/oauth_session_spec.rb b/spec/lib/gitlab/geo/oauth_session_spec.rb
index f20d7e5f57d87feb5e8120e94cafc2cb5c157ff0..77dd0e4da55c3a77f400f3b543bf5c1d36f0089e 100644
--- a/spec/lib/gitlab/geo/oauth_session_spec.rb
+++ b/spec/lib/gitlab/geo/oauth_session_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
 describe Gitlab::Geo::OauthSession do
   subject { described_class.new }
   let(:oauth_app) { FactoryGirl.create(:doorkeeper_application) }
-  let(:oauth_return_to) { 'http://localhost:3000/oath/geo/callback' }
+  let(:oauth_return_to) { 'http://localhost:3000/oauth/geo/callback' }
   let(:dummy_state) { 'salt:hmac:return_to' }
   let(:valid_state) { described_class.new(return_to: oauth_return_to).generate_oauth_state }
   let(:access_token) { FactoryGirl.create(:doorkeeper_access_token).token }
diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
index 0a7ca3ec848be174fe4e7418957b74fa3c8ca6d3..0af249d8690edb80bceaae9fb7bf6b22323cc2d5 100644
--- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
@@ -33,8 +33,8 @@ describe Gitlab::Gfm::ReferenceRewriter do
         end
 
         it { is_expected.to include issue_first.to_reference(new_project) }
-        it { is_expected.to_not include issue_second.to_reference(new_project) }
-        it { is_expected.to_not include merge_request.to_reference(new_project) }
+        it { is_expected.not_to include issue_second.to_reference(new_project) }
+        it { is_expected.not_to include merge_request.to_reference(new_project) }
       end
 
       context 'description ambigous elements' do
diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
index eda956e6f0a92cd92613ef7f9749b106f8df7c0a..6eca33f9fee300ea4537d257829874d6dfff3801 100644
--- a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
@@ -32,13 +32,13 @@ describe Gitlab::Gfm::UploadsRewriter do
       let(:new_paths) { new_files.map(&:path) }
 
       it 'rewrites content' do
-        expect(new_text).to_not eq text
+        expect(new_text).not_to eq text
         expect(new_text.length).to eq text.length
       end
 
       it 'copies files' do
         expect(new_files).to all(exist)
-        expect(old_paths).to_not match_array new_paths
+        expect(old_paths).not_to match_array new_paths
         expect(old_paths).to all(include(old_project.path_with_namespace))
         expect(new_paths).to all(include(new_project.path_with_namespace))
       end
@@ -48,8 +48,8 @@ describe Gitlab::Gfm::UploadsRewriter do
       end
 
       it 'generates a new secret for each file' do
-        expect(new_paths).to_not include image_uploader.secret
-        expect(new_paths).to_not include zip_uploader.secret
+        expect(new_paths).not_to include image_uploader.secret
+        expect(new_paths).not_to include zip_uploader.secret
       end
     end
 
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index 7d98ce5ed3e01e34e9cfbd9164300b60bfe71041..1a3807187bee9a2018e0fa365aca1044c5ac7117 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -336,7 +336,7 @@ describe Gitlab::GitAccess, lib: true do
           describe 'git annex disabled' do
             before { allow(Gitlab.config.gitlab_shell).to receive(:git_annex_enabled).and_return(false) }
 
-            it { expect(access.push_access_check(git_annex_changes)).to_not be_allowed }
+            it { expect(access.push_access_check(git_annex_changes)).not_to be_allowed }
           end
         end
 
@@ -355,7 +355,7 @@ describe Gitlab::GitAccess, lib: true do
           describe 'git annex disabled' do
             before { allow(Gitlab.config.gitlab_shell).to receive(:git_annex_enabled).and_return(false) }
 
-            it { expect(access.push_access_check(git_annex_changes)).to_not be_allowed }
+            it { expect(access.push_access_check(git_annex_changes)).not_to be_allowed }
           end
         end
       end
@@ -382,7 +382,8 @@ describe Gitlab::GitAccess, lib: true do
 
       it 'allows githook for new branch with an old bad commit' do
         bad_commit = double("Commit", safe_message: 'Some change').as_null_object
-        allow(bad_commit).to receive(:refs).and_return(['heads/master'])
+        ref_object = double(name: 'heads/master')
+        allow(bad_commit).to receive(:refs).and_return([ref_object])
         allow_any_instance_of(Repository).to receive(:commits_between).and_return([bad_commit])
 
         project.create_git_hook
@@ -394,7 +395,8 @@ describe Gitlab::GitAccess, lib: true do
 
       it 'allows githook for any change with an old bad commit' do
         bad_commit = double("Commit", safe_message: 'Some change').as_null_object
-        allow(bad_commit).to receive(:refs).and_return(['heads/master'])
+        ref_object = double(name: 'heads/master')
+        allow(bad_commit).to receive(:refs).and_return([ref_object])
         allow_any_instance_of(Repository).to receive(:commits_between).and_return([bad_commit])
 
         project.create_git_hook
@@ -403,6 +405,20 @@ describe Gitlab::GitAccess, lib: true do
         # push to new branch, so use a blank old rev and new ref
         expect(access.git_hook_check(user, project, 'refs/heads/master', '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9', '570e7b2abdd848b95f2f578043fc23bd6f6fd24d')).to be_allowed
       end
+
+      it 'does not allow any change from Web UI with bad commit' do
+        bad_commit = double("Commit", safe_message: 'Some change').as_null_object
+        # We use tmp ref a a temporary for Web UI commiting
+        ref_object = double(name: 'refs/tmp')
+        allow(bad_commit).to receive(:refs).and_return([ref_object])
+        allow_any_instance_of(Repository).to receive(:commits_between).and_return([bad_commit])
+
+        project.create_git_hook
+        project.git_hook.update(commit_message_regex: "Change some files")
+
+        # push to new branch, so use a blank old rev and new ref
+        expect(access.git_hook_check(user, project, 'refs/heads/master', '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9', '570e7b2abdd848b95f2f578043fc23bd6f6fd24d')).not_to be_allowed
+      end
     end
 
     describe "member_check" do
diff --git a/spec/lib/gitlab/github_import/comment_formatter_spec.rb b/spec/lib/gitlab/github_import/comment_formatter_spec.rb
index 55e86d4ceac88fcc1e242d779cbe52db8023864a..9ae02a6c45fbb047f08a2b4836e1af86d2f68c32 100644
--- a/spec/lib/gitlab/github_import/comment_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/comment_formatter_spec.rb
@@ -29,6 +29,7 @@ describe Gitlab::GithubImport::CommentFormatter, lib: true do
           commit_id: nil,
           line_code: nil,
           author_id: project.creator_id,
+          type: nil,
           created_at: created_at,
           updated_at: updated_at
         }
@@ -56,6 +57,7 @@ describe Gitlab::GithubImport::CommentFormatter, lib: true do
           commit_id: '6dcb09b5b57875f334f61aebed695e2e4193db5e',
           line_code: 'ce1be0ff4065a6e9415095c95f25f47a633cef2b_4_3',
           author_id: project.creator_id,
+          type: 'LegacyDiffNote',
           created_at: created_at,
           updated_at: updated_at
         }
diff --git a/spec/lib/gitlab/github_import/hook_formatter_spec.rb b/spec/lib/gitlab/github_import/hook_formatter_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..110ba428258eceb74d2c2ebbdc6cc9747a2444ef
--- /dev/null
+++ b/spec/lib/gitlab/github_import/hook_formatter_spec.rb
@@ -0,0 +1,65 @@
+require 'spec_helper'
+
+describe Gitlab::GithubImport::HookFormatter, lib: true do
+  describe '#id' do
+    it 'returns raw id' do
+      raw = double(id: 100000)
+      formatter = described_class.new(raw)
+      expect(formatter.id).to eq 100000
+    end
+  end
+
+  describe '#name' do
+    it 'returns raw id' do
+      raw = double(name: 'web')
+      formatter = described_class.new(raw)
+      expect(formatter.name).to eq 'web'
+    end
+  end
+
+  describe '#config' do
+    it 'returns raw config.attrs' do
+      raw = double(config: double(attrs: { url: 'http://something.com/webhook' }))
+      formatter = described_class.new(raw)
+      expect(formatter.config).to eq({ url: 'http://something.com/webhook' })
+    end
+  end
+
+  describe '#valid?' do
+    it 'returns true when events contains the wildcard event' do
+      raw = double(events: ['*', 'commit_comment'], active: true)
+      formatter = described_class.new(raw)
+      expect(formatter.valid?).to eq true
+    end
+
+    it 'returns true when events contains the create event' do
+      raw = double(events: ['create', 'commit_comment'], active: true)
+      formatter = described_class.new(raw)
+      expect(formatter.valid?).to eq true
+    end
+
+    it 'returns true when events contains delete event' do
+      raw = double(events: ['delete', 'commit_comment'], active: true)
+      formatter = described_class.new(raw)
+      expect(formatter.valid?).to eq true
+    end
+
+    it 'returns true when events contains pull_request event' do
+      raw = double(events: ['pull_request', 'commit_comment'], active: true)
+      formatter = described_class.new(raw)
+      expect(formatter.valid?).to eq true
+    end
+
+    it 'returns false when events does not contains branch related events' do
+      raw = double(events: ['member', 'commit_comment'], active: true)
+      formatter = described_class.new(raw)
+      expect(formatter.valid?).to eq false
+    end
+
+    it 'returns false when hook is not active' do
+      raw = double(events: ['pull_request', 'commit_comment'], active: false)
+      formatter = described_class.new(raw)
+      expect(formatter.valid?).to eq false
+    end
+  end
+end
diff --git a/spec/lib/gitlab/gitignore_spec.rb b/spec/lib/gitlab/gitignore_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..72baa516cc4ddd289f1a77de9027aa347010d8df
--- /dev/null
+++ b/spec/lib/gitlab/gitignore_spec.rb
@@ -0,0 +1,40 @@
+require 'spec_helper'
+
+describe Gitlab::Gitignore do
+  subject { Gitlab::Gitignore }
+
+  describe '.all' do
+    it 'strips the gitignore suffix' do
+      expect(subject.all.first.name).not_to end_with('.gitignore')
+    end
+
+    it 'combines the globals and rest' do
+      all = subject.all.map(&:name)
+
+      expect(all).to include('Vim')
+      expect(all).to include('Ruby')
+    end
+  end
+
+  describe '.find' do
+    it 'returns nil if the file does not exist' do
+      expect(subject.find('mepmep-yadida')).to be nil
+    end
+
+    it 'returns the Gitignore object of a valid file' do
+      ruby = subject.find('Ruby')
+
+      expect(ruby).to be_a Gitlab::Gitignore
+      expect(ruby.name).to eq('Ruby')
+    end
+  end
+
+  describe '#content' do
+    it 'loads the full file' do
+      gitignore = subject.new(Rails.root.join('vendor/gitignore/Ruby.gitignore'))
+
+      expect(gitignore.name).to eq 'Ruby'
+      expect(gitignore.content).to start_with('*.gem')
+    end
+  end
+end
diff --git a/spec/lib/gitlab/gitlab_import/client_spec.rb b/spec/lib/gitlab/gitlab_import/client_spec.rb
index e6831e7c3832ba535cb0059c3c009e094cc19851..cd8e805466a70e4943ea10f338cef8dc1da5c6b3 100644
--- a/spec/lib/gitlab/gitlab_import/client_spec.rb
+++ b/spec/lib/gitlab/gitlab_import/client_spec.rb
@@ -1,11 +1,13 @@
 require 'spec_helper'
 
 describe Gitlab::GitlabImport::Client, lib: true do
+  include ImportSpecHelper
+
   let(:token) { '123456' }
   let(:client) { Gitlab::GitlabImport::Client.new(token) }
 
   before do
-    Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "gitlab")
+    stub_omniauth_provider('gitlab')
   end
 
   it 'all OAuth2 client options are symbols' do
diff --git a/spec/lib/gitlab/lazy_spec.rb b/spec/lib/gitlab/lazy_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b5ca89dd242390a7cef3d539969d41f094373e81
--- /dev/null
+++ b/spec/lib/gitlab/lazy_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+
+describe Gitlab::Lazy, lib: true do
+  let(:dummy) { double(:dummy) }
+
+  context 'when not calling any methods' do
+    it 'does not call the supplied block' do
+      expect(dummy).not_to receive(:foo)
+
+      described_class.new { dummy.foo }
+    end
+  end
+
+  context 'when calling a method on the object' do
+    it 'lazy loads the value returned by the block' do
+      expect(dummy).to receive(:foo).and_return('foo')
+
+      lazy = described_class.new { dummy.foo }
+
+      expect(lazy.to_s).to eq('foo')
+    end
+  end
+
+  describe '#respond_to?' do
+    it 'returns true for a method defined on the wrapped object' do
+      lazy = described_class.new { 'foo' }
+
+      expect(lazy).to respond_to(:downcase)
+    end
+
+    it 'returns false for a method not defined on the wrapped object' do
+      lazy = described_class.new { 'foo' }
+
+      expect(lazy).not_to respond_to(:quack)
+    end
+  end
+end
diff --git a/spec/lib/gitlab/ldap/group_sync_spec.rb b/spec/lib/gitlab/ldap/group_sync_spec.rb
index 197327820e7c9b8d03d24562e2235b36798f9196..46ff87a9172ab1ad94c70ced50f59b2e9d4d544c 100644
--- a/spec/lib/gitlab/ldap/group_sync_spec.rb
+++ b/spec/lib/gitlab/ldap/group_sync_spec.rb
@@ -45,8 +45,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
     let(:group1) { create(:group) }
     let(:group2) { create(:group) }
     let(:ldap_group1) do
-      Net::LDAP::Entry.from_single_ldif_string(
-        <<-EOS.strip_heredoc
+      Net::LDAP::Entry.from_single_ldif_string(<<-EOS.strip_heredoc)
           dn: cn=ldap_group1,ou=groups,dc=example,dc=com
           cn: ldap_group1
           description: LDAP Group 1
@@ -56,7 +55,6 @@ describe Gitlab::LDAP::GroupSync, lib: true do
           objectclass: top
           objectclass: groupOfNames
         EOS
-      )
     end
 
     context 'with all functionality against one LDAP group type' do
@@ -183,8 +181,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
             Gitlab::LDAP::GroupSync.new('ldapsecondary', adapter)
           end
           let(:ldap_secondary_group1) do
-            Net::LDAP::Entry.from_single_ldif_string(
-              <<-EOS.strip_heredoc
+            Net::LDAP::Entry.from_single_ldif_string(<<-EOS.strip_heredoc)
                 dn: cn=ldap_secondary_group1,ou=groups,dc=example,dc=com
                 cn: ldap_secondary_group1
                 description: LDAP Group 1
@@ -194,7 +191,6 @@ describe Gitlab::LDAP::GroupSync, lib: true do
                 objectclass: top
                 objectclass: groupOfNames
               EOS
-            )
           end
           let(:user_w_multiple_ids) { create(:user) }
 
@@ -247,8 +243,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
 
       context 'when access level spillover could happen' do
         it 'does not erroneously add users' do
-          ldap_group2 = Net::LDAP::Entry.from_single_ldif_string(
-            <<-EOS.strip_heredoc
+          ldap_group2 = Net::LDAP::Entry.from_single_ldif_string(<<-EOS.strip_heredoc)
               dn: cn=ldap_group2,ou=groups,dc=example,dc=com
               cn: ldap_group2
               description: LDAP Group 2
@@ -257,7 +252,6 @@ describe Gitlab::LDAP::GroupSync, lib: true do
               objectclass: top
               objectclass: groupOfNames
             EOS
-          )
 
           allow_any_instance_of(Gitlab::LDAP::Group)
             .to receive(:adapter).and_return(adapter)
@@ -332,8 +326,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
       context 'with groupOfNames style LDAP group' do
         let(:ldap_group) do
           Gitlab::LDAP::Group.new(
-            Net::LDAP::Entry.from_single_ldif_string(
-              <<-EOS.strip_heredoc
+            Net::LDAP::Entry.from_single_ldif_string(<<-EOS.strip_heredoc)
                 dn: cn=ldap_group1,ou=groups,dc=example,dc=com
                 cn: ldap_group1
                 description: LDAP Group 1
@@ -341,7 +334,6 @@ describe Gitlab::LDAP::GroupSync, lib: true do
                 objectclass: top
                 objectclass: groupOfNames
               EOS
-            )
           )
         end
 
@@ -356,8 +348,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
       context 'with posixGroup style LDAP group' do
         let(:ldap_group) do
           Gitlab::LDAP::Group.new(
-            Net::LDAP::Entry.from_single_ldif_string(
-              <<-EOS.strip_heredoc
+            Net::LDAP::Entry.from_single_ldif_string(<<-EOS.strip_heredoc)
                 dn: cn=ldap_group1,ou=groups,dc=example,dc=com
                 cn: ldap_group1
                 description: LDAP Group 1
@@ -365,7 +356,6 @@ describe Gitlab::LDAP::GroupSync, lib: true do
                 objectclass: top
                 objectclass: posixGroup
               EOS
-            )
           )
         end
         let(:ldap_user) do
@@ -453,8 +443,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
       context 'with groupOfUniqueNames style LDAP group' do
         let(:ldap_group) do
           Gitlab::LDAP::Group.new(
-            Net::LDAP::Entry.from_single_ldif_string(
-              <<-EOS.strip_heredoc
+            Net::LDAP::Entry.from_single_ldif_string(<<-EOS.strip_heredoc)
                 dn: cn=ldap_group1,ou=groups,dc=example,dc=com
                 cn: ldap_group1
                 description: LDAP Group 1
@@ -462,7 +451,6 @@ describe Gitlab::LDAP::GroupSync, lib: true do
                 objectclass: top
                 objectclass: groupOfUniqueNames
               EOS
-            )
           )
         end
 
@@ -476,15 +464,13 @@ describe Gitlab::LDAP::GroupSync, lib: true do
       context 'with an empty LDAP group' do
         let(:ldap_group) do
           Gitlab::LDAP::Group.new(
-            Net::LDAP::Entry.from_single_ldif_string(
-              <<-EOS.strip_heredoc
+            Net::LDAP::Entry.from_single_ldif_string(<<-EOS.strip_heredoc)
                 dn: cn=ldap_group1,ou=groups,dc=example,dc=com
                 cn: ldap_group1
                 description: LDAP Group 1
                 objectclass: top
                 objectclass: groupOfUniqueNames
               EOS
-            )
           )
         end
 
@@ -498,8 +484,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
       context 'with uid=username member format' do
         let(:ldap_group) do
           Gitlab::LDAP::Group.new(
-            Net::LDAP::Entry.from_single_ldif_string(
-              <<-EOS.strip_heredoc
+            Net::LDAP::Entry.from_single_ldif_string(<<-EOS.strip_heredoc)
                 dn: cn=ldap_group1,ou=groups,dc=example,dc=com
                 cn: ldap_group1
                 member: uid=#{user1.username}
@@ -507,7 +492,6 @@ describe Gitlab::LDAP::GroupSync, lib: true do
                 objectclass: top
                 objectclass: groupOfUniqueNames
               EOS
-            )
           )
         end
         let(:ldap_user) do
@@ -548,6 +532,38 @@ describe Gitlab::LDAP::GroupSync, lib: true do
             }.from(nil).to(user1.username)
         end
       end
+
+      context 'with invalid DNs in the LDAP group' do
+        let(:ldap_group) do
+          Gitlab::LDAP::Group.new(
+            Net::LDAP::Entry.from_single_ldif_string(<<-EOS.strip_heredoc)
+                dn: cn=ldap_group1,ou=groups,dc=example,dc=com
+                cn: ldap_group1
+                member:
+                member: uid=#{user1.username},ou=users,dc=example,dc=com
+                member: foo, bar
+                description: LDAP Group 1
+                objectclass: top
+                objectclass: groupOfUniqueNames
+              EOS
+          )
+        end
+
+        # Check that the blank member and malformed member logged an error
+        it 'expects two errors to be logged' do
+          expect(Rails.logger).to receive(:error) do |&block|
+            expect(block.call).to match /^Found malformed DN:/
+          end.twice
+
+          group_sync.sync_groups
+        end
+
+        it 'expects the valid user to be added' do
+          expect { group_sync.sync_groups }
+            .to change { group1.members.where(user_id: user1.id).any? }
+              .from(false).to(true)
+        end
+      end
     end
   end
 
@@ -557,8 +573,7 @@ describe Gitlab::LDAP::GroupSync, lib: true do
     let(:user3) { create(:user) }
 
     let(:admin_group) do
-      Net::LDAP::Entry.from_single_ldif_string(
-        <<-EOS.strip_heredoc
+      Net::LDAP::Entry.from_single_ldif_string(<<-EOS.strip_heredoc)
           dn: cn=admin_group,ou=groups,dc=example,dc=com
           cn: admin_group
           description: Admin Group
@@ -567,14 +582,11 @@ describe Gitlab::LDAP::GroupSync, lib: true do
           objectclass: top
           objectclass: groupOfNames
         EOS
-      )
     end
 
     before do
-      user1.admin = true
-      user1.save
-      user3.admin = true
-      user3.save
+      user1.update_attribute(:admin, true)
+      user3.update_attribute(:admin, true)
 
       allow_any_instance_of(Gitlab::LDAP::Group)
         .to receive(:adapter).and_return(adapter)
@@ -612,4 +624,86 @@ describe Gitlab::LDAP::GroupSync, lib: true do
         .not_to change { User.admins.where(id: user3.id).any? }
     end
   end
+
+  describe '#sync_external_users' do
+    let(:user1) { create(:user) }
+    let(:user2) { create(:user) }
+    let(:user3) { create(:user) }
+    let(:user4) { create(:user) }
+
+    let(:external_group1) do
+      Net::LDAP::Entry.from_single_ldif_string(<<-EOS.strip_heredoc)
+          dn: cn=external_group1,ou=groups,dc=example,dc=com
+          cn: external_group1
+          description: External Group 1
+          gidnumber: 42
+          uniqueMember: uid=#{user1.username},ou=users,dc=example,dc=com
+          objectclass: top
+          objectclass: groupOfNames
+      EOS
+    end
+
+    let(:external_group2) do
+      Net::LDAP::Entry.from_single_ldif_string(<<-EOS.strip_heredoc)
+        dn: cn=external_group2,ou=groups,dc=example,dc=com
+        cn: external_group2
+        description: External Group 2
+        gidnumber: 42
+        uniqueMember: uid=#{user2.username},ou=users,dc=example,dc=com
+        objectclass: top
+        objectclass: groupOfNames
+      EOS
+    end
+
+    before do
+      user3.update_attribute(:external, true)
+      user4.update_attribute(:external, true)
+
+      allow_any_instance_of(Gitlab::LDAP::Group)
+        .to receive(:adapter).and_return(adapter)
+      allow(Gitlab::LDAP::Group)
+        .to receive(:find_by_cn)
+              .with('external_group1', kind_of(Gitlab::LDAP::Adapter))
+              .and_return(Gitlab::LDAP::Group.new(external_group1))
+      allow(Gitlab::LDAP::Group)
+        .to receive(:find_by_cn)
+              .with('external_group2', kind_of(Gitlab::LDAP::Adapter))
+              .and_return(Gitlab::LDAP::Group.new(external_group2))
+
+      user1.identities.create(
+        provider: 'ldapmain',
+        extern_uid: "uid=#{user1.username},ou=users,dc=example,dc=com"
+      )
+      user2.identities.create(
+        provider: 'ldapmain',
+        extern_uid: "uid=#{user2.username},ou=users,dc=example,dc=com"
+      )
+      user4.identities.create(
+        provider: 'ldapmain',
+        extern_uid: "uid=#{user4.username},ou=users,dc=example,dc=com"
+      )
+
+      allow(group_sync).to receive_messages(external_groups: %w(external_group1 external_group2))
+    end
+
+    it 'adds user1 as external user' do
+      expect { group_sync.sync_external_users }
+        .to change { User.external.where(id: user1.id).any? }.from(false).to(true)
+    end
+
+    it 'adds user2 as external user' do
+      expect { group_sync.sync_external_users }
+        .to change { User.external.where(id: user2.id).any? }.from(false).to(true)
+    end
+
+    it 'removes users that are not in the LDAP group' do
+      expect { group_sync.sync_external_users }
+        .to change { User.external.where(id: user4.id).any? }.from(true).to(false)
+    end
+
+    it 'leaves as external users that do not have the LDAP provider' do
+      expect { group_sync.sync_external_users }
+        .not_to change { User.external.where(id: user3.id).any? }
+    end
+  end
 end
diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb
index 3325190789baf4864e15908e356d0df8794dba62..88814bc474d15cc4174f90e3ac2ddd73c3026dc0 100644
--- a/spec/lib/gitlab/lfs/lfs_router_spec.rb
+++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb
@@ -368,7 +368,7 @@ describe Gitlab::Lfs::Router, lib: true do
               expect(response['objects']).to be_kind_of(Array)
               expect(response['objects'].first['oid']).to eq(sample_oid)
               expect(response['objects'].first['size']).to eq(sample_size)
-              expect(lfs_object.projects.pluck(:id)).to_not include(project.id)
+              expect(lfs_object.projects.pluck(:id)).not_to include(project.id)
               expect(lfs_object.projects.pluck(:id)).to include(public_project.id)
               expect(response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}")
               expect(response['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth)
@@ -430,7 +430,7 @@ describe Gitlab::Lfs::Router, lib: true do
 
               expect(response_body['objects'].last['oid']).to eq(sample_oid)
               expect(response_body['objects'].last['size']).to eq(sample_size)
-              expect(response_body['objects'].last).to_not have_key('actions')
+              expect(response_body['objects'].last).not_to have_key('actions')
             end
           end
         end
diff --git a/spec/lib/gitlab/metrics/instrumentation_spec.rb b/spec/lib/gitlab/metrics/instrumentation_spec.rb
index 7b86450a2239f5c8e6937aa5078d8ef04528f64d..cdf641341cbe6ba56b7c2d12c21477962a99b0b7 100644
--- a/spec/lib/gitlab/metrics/instrumentation_spec.rb
+++ b/spec/lib/gitlab/metrics/instrumentation_spec.rb
@@ -9,9 +9,31 @@ describe Gitlab::Metrics::Instrumentation do
         text
       end
 
+      class << self
+        def buzz(text = 'buzz')
+          text
+        end
+        private :buzz
+
+        def flaky(text = 'flaky')
+          text
+        end
+        protected :flaky
+      end
+
       def bar(text = 'bar')
         text
       end
+
+      def wadus(text = 'wadus')
+        text
+      end
+      private :wadus
+
+      def chaf(text = 'chaf')
+        text
+      end
+      protected :chaf
     end
 
     allow(@dummy).to receive(:name).and_return('Dummy')
@@ -57,7 +79,7 @@ describe Gitlab::Metrics::Instrumentation do
           and_return(transaction)
 
         expect(transaction).to receive(:add_metric).
-          with(described_class::SERIES, an_instance_of(Hash),
+          with(described_class::SERIES, hash_including(:duration, :cpu_duration),
                method: 'Dummy.foo')
 
         @dummy.foo
@@ -67,7 +89,7 @@ describe Gitlab::Metrics::Instrumentation do
         allow(Gitlab::Metrics).to receive(:method_call_threshold).
           and_return(100)
 
-        expect(transaction).to_not receive(:add_metric)
+        expect(transaction).not_to receive(:add_metric)
 
         @dummy.foo
       end
@@ -137,7 +159,7 @@ describe Gitlab::Metrics::Instrumentation do
           and_return(transaction)
 
         expect(transaction).to receive(:add_metric).
-          with(described_class::SERIES, an_instance_of(Hash),
+          with(described_class::SERIES, hash_including(:duration, :cpu_duration),
                method: 'Dummy#bar')
 
         @dummy.new.bar
@@ -147,7 +169,7 @@ describe Gitlab::Metrics::Instrumentation do
         allow(Gitlab::Metrics).to receive(:method_call_threshold).
           and_return(100)
 
-        expect(transaction).to_not receive(:add_metric)
+        expect(transaction).not_to receive(:add_metric)
 
         @dummy.new.bar
       end
@@ -208,6 +230,21 @@ describe Gitlab::Metrics::Instrumentation do
       described_class.instrument_methods(@dummy)
 
       expect(described_class.instrumented?(@dummy.singleton_class)).to eq(true)
+      expect(@dummy.method(:foo).source_location.first).to match(/instrumentation\.rb/)
+    end
+
+    it 'instruments all protected class methods' do
+      described_class.instrument_methods(@dummy)
+
+      expect(described_class.instrumented?(@dummy.singleton_class)).to eq(true)
+      expect(@dummy.method(:flaky).source_location.first).to match(/instrumentation\.rb/)
+    end
+
+    it 'instruments all private instance methods' do
+      described_class.instrument_methods(@dummy)
+
+      expect(described_class.instrumented?(@dummy.singleton_class)).to eq(true)
+      expect(@dummy.method(:buzz).source_location.first).to match(/instrumentation\.rb/)
     end
 
     it 'only instruments methods directly defined in the module' do
@@ -220,7 +257,7 @@ describe Gitlab::Metrics::Instrumentation do
 
       described_class.instrument_methods(@dummy)
 
-      expect(@dummy).to_not respond_to(:_original_kittens)
+      expect(@dummy).not_to respond_to(:_original_kittens)
     end
 
     it 'can take a block to determine if a method should be instrumented' do
@@ -228,7 +265,7 @@ describe Gitlab::Metrics::Instrumentation do
         false
       end
 
-      expect(@dummy).to_not respond_to(:_original_foo)
+      expect(@dummy).not_to respond_to(:_original_foo)
     end
   end
 
@@ -241,6 +278,21 @@ describe Gitlab::Metrics::Instrumentation do
       described_class.instrument_instance_methods(@dummy)
 
       expect(described_class.instrumented?(@dummy)).to eq(true)
+      expect(@dummy.new.method(:bar).source_location.first).to match(/instrumentation\.rb/)
+    end
+
+    it 'instruments all protected instance methods' do
+      described_class.instrument_instance_methods(@dummy)
+
+      expect(described_class.instrumented?(@dummy)).to eq(true)
+      expect(@dummy.new.method(:chaf).source_location.first).to match(/instrumentation\.rb/)
+    end
+
+    it 'instruments all private instance methods' do
+      described_class.instrument_instance_methods(@dummy)
+
+      expect(described_class.instrumented?(@dummy)).to eq(true)
+      expect(@dummy.new.method(:wadus).source_location.first).to match(/instrumentation\.rb/)
     end
 
     it 'only instruments methods directly defined in the module' do
@@ -253,7 +305,7 @@ describe Gitlab::Metrics::Instrumentation do
 
       described_class.instrument_instance_methods(@dummy)
 
-      expect(@dummy.method_defined?(:_original_kittens)).to eq(false)
+      expect(@dummy.new.method(:kittens).source_location.first).not_to match(/instrumentation\.rb/)
     end
 
     it 'can take a block to determine if a method should be instrumented' do
@@ -261,7 +313,7 @@ describe Gitlab::Metrics::Instrumentation do
         false
       end
 
-      expect(@dummy.method_defined?(:_original_bar)).to eq(false)
+      expect(@dummy.new.method(:bar).source_location.first).not_to match(/instrumentation\.rb/)
     end
   end
 end
diff --git a/spec/lib/gitlab/metrics/rack_middleware_spec.rb b/spec/lib/gitlab/metrics/rack_middleware_spec.rb
index b99be4e1060025780d2cfedc72f1d93e148b1dcd..40289f8b97230cc83aa53c6a82db6f1c1fe2e2fc 100644
--- a/spec/lib/gitlab/metrics/rack_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/rack_middleware_spec.rb
@@ -31,6 +31,20 @@ describe Gitlab::Metrics::RackMiddleware do
 
       middleware.call(env)
     end
+
+    it 'tags a transaction with the method andpath of the route in the grape endpoint' do
+      route    = double(:route, route_method: "GET", route_path: "/:version/projects/:id/archive(.:format)")
+      endpoint = double(:endpoint, route: route)
+
+      env['api.endpoint'] = endpoint
+
+      allow(app).to receive(:call).with(env)
+
+      expect(middleware).to receive(:tag_endpoint).
+        with(an_instance_of(Gitlab::Metrics::Transaction), env)
+
+      middleware.call(env)
+    end
   end
 
   describe '#transaction_from_env' do
@@ -60,4 +74,19 @@ describe Gitlab::Metrics::RackMiddleware do
       expect(transaction.action).to eq('TestController#show')
     end
   end
+
+  describe '#tag_endpoint' do
+    let(:transaction) { middleware.transaction_from_env(env) }
+
+    it 'tags a transaction with the method and path of the route in the grape endpount' do
+      route    = double(:route, route_method: "GET", route_path: "/:version/projects/:id/archive(.:format)")
+      endpoint = double(:endpoint, route: route)
+
+      env['api.endpoint'] = endpoint
+
+      middleware.tag_endpoint(transaction, env)
+
+      expect(transaction.action).to eq('Grape#GET /projects/:id/archive')
+    end
+  end
 end
diff --git a/spec/lib/gitlab/metrics/sampler_spec.rb b/spec/lib/gitlab/metrics/sampler_spec.rb
index 38da77adc9f17fe9db4dd60dabb53668b942b1ff..1ab923b58cf6881b901a5c0445b1231ab00648b9 100644
--- a/spec/lib/gitlab/metrics/sampler_spec.rb
+++ b/spec/lib/gitlab/metrics/sampler_spec.rb
@@ -72,14 +72,25 @@ describe Gitlab::Metrics::Sampler do
     end
   end
 
-  describe '#sample_objects' do
-    it 'adds a metric containing the amount of allocated objects' do
-      expect(sampler).to receive(:add_metric).
-        with(/object_counts/, an_instance_of(Hash), an_instance_of(Hash)).
-        at_least(:once).
-        and_call_original
+  if Gitlab::Metrics.mri?
+    describe '#sample_objects' do
+      it 'adds a metric containing the amount of allocated objects' do
+        expect(sampler).to receive(:add_metric).
+          with(/object_counts/, an_instance_of(Hash), an_instance_of(Hash)).
+          at_least(:once).
+          and_call_original
+
+        sampler.sample_objects
+      end
 
-      sampler.sample_objects
+      it 'ignores classes without a name' do
+        expect(Allocations).to receive(:to_hash).and_return({ Class.new => 4 })
+
+        expect(sampler).not_to receive(:add_metric).
+          with('object_counts', an_instance_of(Hash), type: nil)
+
+        sampler.sample_objects
+      end
     end
   end
 
@@ -130,7 +141,7 @@ describe Gitlab::Metrics::Sampler do
       100.times do
         interval = sampler.sleep_interval
 
-        expect(interval).to_not eq(last)
+        expect(interval).not_to eq(last)
 
         last = interval
       end
diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
index e3293a012078ade4ad825bbbaebeddf0666d8b21..49699ffe28f0a1875e8b1a2134f7ad23104ac7eb 100644
--- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
@@ -13,7 +13,7 @@ describe Gitlab::Metrics::Subscribers::ActiveRecord do
     describe 'without a current transaction' do
       it 'simply returns' do
         expect_any_instance_of(Gitlab::Metrics::Transaction).
-          to_not receive(:increment)
+          not_to receive(:increment)
 
         subscriber.sql(event)
       end
diff --git a/spec/lib/gitlab/middleware/rails_queue_duration_spec.rb b/spec/lib/gitlab/middleware/rails_queue_duration_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fd6f684db0c17bf1244fd9a4573ce4e8231bb045
--- /dev/null
+++ b/spec/lib/gitlab/middleware/rails_queue_duration_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe Gitlab::Middleware::RailsQueueDuration do
+  let(:app) { double(:app) }
+  let(:middleware) { described_class.new(app) }
+  let(:env) { {} }
+  let(:transaction) { double(:transaction) }
+
+  before { expect(app).to receive(:call).with(env).and_return('yay') }
+
+  describe '#call' do
+    it 'calls the app when metrics are disabled' do
+      expect(Gitlab::Metrics).to receive(:current_transaction).and_return(nil)
+      expect(middleware.call(env)).to eq('yay')
+    end
+
+    context 'when metrics are enabled' do
+      before { allow(Gitlab::Metrics).to receive(:current_transaction).and_return(transaction) }
+
+      it 'calls the app when metrics are enabled but no timing header is found' do
+        expect(middleware.call(env)).to eq('yay')
+      end
+
+      it 'sets proxy_flight_time and calls the app when the header is present' do
+        env['HTTP_GITLAB_WORHORSE_PROXY_START'] = '123'
+        expect(transaction).to receive(:set).with(:rails_queue_duration, an_instance_of(Float))
+        expect(middleware.call(env)).to eq('yay')
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/note_data_builder_spec.rb b/spec/lib/gitlab/note_data_builder_spec.rb
index f093d0a0d8b3a7dd5b481841cf7e92d68abdc783..e848d88182fb07b030684e283ce80211d7563cd4 100644
--- a/spec/lib/gitlab/note_data_builder_spec.rb
+++ b/spec/lib/gitlab/note_data_builder_spec.rb
@@ -9,7 +9,8 @@ describe 'Gitlab::NoteDataBuilder', lib: true do
   before(:each) do
     expect(data).to have_key(:object_attributes)
     expect(data[:object_attributes]).to have_key(:url)
-    expect(data[:object_attributes][:url]).to eq(Gitlab::UrlBuilder.build(note))
+    expect(data[:object_attributes][:url])
+      .to eq(Gitlab::UrlBuilder.build(note))
     expect(data[:object_kind]).to eq('note')
     expect(data[:user]).to eq(user.hook_attrs)
   end
@@ -37,13 +38,21 @@ describe 'Gitlab::NoteDataBuilder', lib: true do
   end
 
   describe 'When asking for a note on issue' do
-    let(:issue) { create(:issue, created_at: fixed_time, updated_at: fixed_time) }
-    let(:note) { create(:note_on_issue, noteable_id: issue.id, project: project) }
+    let(:issue) do
+      create(:issue, created_at: fixed_time, updated_at: fixed_time,
+                     project: project)
+    end
+
+    let(:note) do
+      create(:note_on_issue, noteable: issue, project: project)
+    end
 
     it 'returns the note and issue-specific data' do
       expect(data).to have_key(:issue)
-      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']
+      expect(data[:issue].except('updated_at'))
+        .to eq(issue.reload.hook_attrs.except('updated_at'))
+      expect(data[:issue]['updated_at'])
+        .to be > issue.hook_attrs['updated_at']
     end
 
     include_examples 'project hook data'
@@ -51,13 +60,23 @@ describe 'Gitlab::NoteDataBuilder', lib: true do
   end
 
   describe 'When asking for a note on merge request' do
-    let(:merge_request) { create(:merge_request, created_at: fixed_time, updated_at: fixed_time) }
-    let(:note) { create(:note_on_merge_request, noteable_id: merge_request.id, project: project) }
+    let(:merge_request) do
+      create(:merge_request, created_at: fixed_time,
+                             updated_at: fixed_time,
+                             source_project: project)
+    end
+
+    let(:note) do
+      create(:note_on_merge_request, noteable: merge_request,
+                                     project: project)
+    end
 
     it 'returns the note and merge request data' do
       expect(data).to have_key(:merge_request)
-      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']
+      expect(data[:merge_request].except('updated_at'))
+        .to eq(merge_request.reload.hook_attrs.except('updated_at'))
+      expect(data[:merge_request]['updated_at'])
+        .to be > merge_request.hook_attrs['updated_at']
     end
 
     include_examples 'project hook data'
@@ -65,13 +84,22 @@ describe 'Gitlab::NoteDataBuilder', lib: true do
   end
 
   describe 'When asking for a note on merge request diff' do
-    let(:merge_request) { create(:merge_request, created_at: fixed_time, updated_at: fixed_time) }
-    let(:note) { create(:note_on_merge_request_diff, noteable_id: merge_request.id, project: project) }
+    let(:merge_request) do
+      create(:merge_request, created_at: fixed_time, updated_at: fixed_time,
+                             source_project: project)
+    end
+
+    let(:note) do
+      create(:note_on_merge_request_diff, noteable: merge_request,
+                                          project: project)
+    end
 
     it 'returns the note and merge request diff data' do
       expect(data).to have_key(:merge_request)
-      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']
+      expect(data[:merge_request].except('updated_at'))
+        .to eq(merge_request.reload.hook_attrs.except('updated_at'))
+      expect(data[:merge_request]['updated_at'])
+        .to be > merge_request.hook_attrs['updated_at']
     end
 
     include_examples 'project hook data'
@@ -79,13 +107,22 @@ describe 'Gitlab::NoteDataBuilder', lib: true do
   end
 
   describe 'When asking for a note on project snippet' do
-    let!(:snippet) { create(:project_snippet, created_at: fixed_time, updated_at: fixed_time) }
-    let!(:note) { create(:note_on_project_snippet, noteable_id: snippet.id, project: project) }
+    let!(:snippet) do
+      create(:project_snippet, created_at: fixed_time, updated_at: fixed_time,
+                               project: project)
+    end
+
+    let!(:note) do
+      create(:note_on_project_snippet, noteable: snippet,
+                                       project: project)
+    end
 
     it 'returns the note and project snippet data' do
       expect(data).to have_key(:snippet)
-      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']
+      expect(data[:snippet].except('updated_at'))
+        .to eq(snippet.reload.hook_attrs.except('updated_at'))
+      expect(data[:snippet]['updated_at'])
+        .to be > snippet.hook_attrs['updated_at']
     end
 
     include_examples 'project hook data'
diff --git a/spec/lib/gitlab/path_locks_finder_spec.rb b/spec/lib/gitlab/path_locks_finder_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..061fd9008971ebc3ea284d10b240b6d7b9d4898b
--- /dev/null
+++ b/spec/lib/gitlab/path_locks_finder_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+
+describe Gitlab::PathLocksFinder, lib: true do
+  let(:project) { create :empty_project }
+  let(:user) { create :user }
+  let(:finder) { Gitlab::PathLocksFinder.new(project) }
+
+  it "returns correct lock information" do
+    lock1 = create :path_lock, project: project, path: 'app'
+    lock2 = create :path_lock, project: project, path: 'lib/gitlab/repo.rb'
+
+    expect(finder.get_lock_info('app')).to eq(lock1)
+    expect(finder.get_lock_info('app/models/project.rb')).to eq(lock1)
+    expect(finder.get_lock_info('lib')).to be_falsey
+    expect(finder.get_lock_info('lib/gitlab/repo.rb')).to eq(lock2)
+  end
+end
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index db0ff95b4f5c6c83be75122b2ad560f05dea14a4..270b89972d73d1a452e37152085810c84ea151c6 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -43,6 +43,18 @@ describe Gitlab::ProjectSearchResults, lib: true do
       expect(results.issues_count).to eq 1
     end
 
+    it 'should not list project confidential issues for project members with guest role' do
+      project.team << [member, :guest]
+
+      results = described_class.new(member, project, query)
+      issues = results.objects('issues')
+
+      expect(issues).to include issue
+      expect(issues).not_to include security_issue_1
+      expect(issues).not_to include security_issue_2
+      expect(results.issues_count).to eq 1
+    end
+
     it 'should list project confidential issues for author' do
       results = described_class.new(author, project, query)
       issues = results.objects('issues')
diff --git a/spec/lib/gitlab/saml/user_spec.rb b/spec/lib/gitlab/saml/user_spec.rb
index aaeba0799c930990c3a47b6478badbe3002910cc..201914a6ad8c680ea97cc8a9938e6d0820ffb779 100644
--- a/spec/lib/gitlab/saml/user_spec.rb
+++ b/spec/lib/gitlab/saml/user_spec.rb
@@ -198,6 +198,7 @@ describe Gitlab::Saml::User, lib: true do
               allow(ldap_user).to receive(:email) { %w(john@mail.com john2@example.com) }
               allow(ldap_user).to receive(:dn) { 'uid=user1,ou=People,dc=example' }
               allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user)
+              allow(Gitlab::LDAP::Person).to receive(:find_by_dn).and_return(ldap_user)
             end
 
             context 'and no account for the LDAP user' do
@@ -230,6 +231,23 @@ describe Gitlab::Saml::User, lib: true do
                                                           ])
               end
             end
+
+            context 'user has SAML user, and wants to add their LDAP identity' do
+              it 'adds the LDAP identity to the existing SAML user' do
+                create(:omniauth_user, email: 'john@mail.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'saml', username: 'john')
+                local_hash = OmniAuth::AuthHash.new(uid: 'uid=user1,ou=People,dc=example', provider: provider, info: info_hash)
+                local_saml_user = described_class.new(local_hash)
+                local_saml_user.save
+                local_gl_user = local_saml_user.gl_user
+
+                expect(local_gl_user).to be_valid
+                expect(local_gl_user.identities.length).to eql 2
+                identities_as_hash = local_gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
+                expect(identities_as_hash).to match_array([ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' },
+                                                            { provider: 'saml', extern_uid: 'uid=user1,ou=People,dc=example' }
+                                                          ])
+              end
+            end
           end
         end
       end
diff --git a/spec/lib/gitlab/sanitizers/svg_spec.rb b/spec/lib/gitlab/sanitizers/svg_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..030c2063ab20e729e2f868f2a661bc25b5d30cc5
--- /dev/null
+++ b/spec/lib/gitlab/sanitizers/svg_spec.rb
@@ -0,0 +1,94 @@
+require 'spec_helper'
+
+describe Gitlab::Sanitizers::SVG do
+  let(:scrubber) { Gitlab::Sanitizers::SVG::Scrubber.new }
+  let(:namespace) { double(Nokogiri::XML::Namespace, prefix: 'xlink', href: 'http://www.w3.org/1999/xlink') }
+  let(:namespaced_attr) { double(Nokogiri::XML::Attr, name: 'href', namespace: namespace, value: '#awesome_id') }
+
+  describe '.clean' do
+    let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') }
+    let(:data) { open(input_svg_path).read }
+    let(:sanitized_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'sanitized.svg') }
+    let(:sanitized) { open(sanitized_svg_path).read }
+
+    it 'delegates sanitization to scrubber' do
+      expect_any_instance_of(Gitlab::Sanitizers::SVG::Scrubber).to receive(:scrub).at_least(:once)
+      described_class.clean(data)
+    end
+
+    it 'returns sanitized data' do
+      expect(described_class.clean(data)).to eq(sanitized)
+    end
+  end
+
+  context 'scrubber' do
+    describe '#scrub' do
+      let(:invalid_element) { double(Nokogiri::XML::Node, name: 'invalid', value: 'invalid') }
+      let(:invalid_attribute) { double(Nokogiri::XML::Attr, name: 'invalid', namespace: nil) }
+      let(:valid_element) { double(Nokogiri::XML::Node, name: 'use') }
+
+      it 'removes an invalid element' do
+        expect(invalid_element).to receive(:unlink)
+
+        scrubber.scrub(invalid_element)
+      end
+
+      it 'removes an invalid attribute' do
+        allow(valid_element).to receive(:attribute_nodes) { [invalid_attribute] }
+        expect(invalid_attribute).to receive(:unlink)
+
+        scrubber.scrub(valid_element)
+      end
+
+      it 'accepts valid element' do
+        allow(valid_element).to receive(:attribute_nodes) { [namespaced_attr] }
+        expect(valid_element).not_to receive(:unlink)
+
+        scrubber.scrub(valid_element)
+      end
+
+      it 'accepts valid namespaced attributes' do
+        allow(valid_element).to receive(:attribute_nodes) { [namespaced_attr] }
+        expect(namespaced_attr).not_to receive(:unlink)
+
+        scrubber.scrub(valid_element)
+      end
+    end
+
+    describe '#attribute_name_with_namespace' do
+      it 'returns name with prefix when attribute is namespaced' do
+        expect(scrubber.attribute_name_with_namespace(namespaced_attr)).to eq('xlink:href')
+      end
+    end
+
+    describe '#unsafe_href?' do
+      let(:unsafe_attr) { double(Nokogiri::XML::Attr, name: 'href', namespace: namespace, value: 'http://evilsite.example.com/random.svg') }
+
+      it 'returns true if href attribute is an external url' do
+        expect(scrubber.unsafe_href?(unsafe_attr)).to be_truthy
+      end
+
+      it 'returns false if href atttribute is an internal reference' do
+        expect(scrubber.unsafe_href?(namespaced_attr)).to be_falsey
+      end
+    end
+
+    describe '#data_attribute?' do
+      let(:data_attr) { double(Nokogiri::XML::Attr, name: 'data-gitlab', namespace: nil, value: 'gitlab is awesome') }
+      let(:namespaced_attr) { double(Nokogiri::XML::Attr, name: 'data-gitlab', namespace: namespace, value: 'gitlab is awesome') }
+      let(:other_attr) { double(Nokogiri::XML::Attr, name: 'something', namespace: nil, value: 'content') }
+
+      it 'returns true if is a valid data attribute' do
+        expect(scrubber.data_attribute?(data_attr)).to be_truthy
+      end
+
+      it 'returns false if attribute is namespaced' do
+        expect(scrubber.data_attribute?(namespaced_attr)).to be_falsey
+      end
+
+      it 'returns false if not a data attribute' do
+        expect(scrubber.data_attribute?(other_attr)).to be_falsey
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb
index f4afe597e8d8c415c40b5f3bc48c2e00ac3be71f..1bb444bf34fc2101d67e6b9e2c4ce6a5bc3fb0bc 100644
--- a/spec/lib/gitlab/search_results_spec.rb
+++ b/spec/lib/gitlab/search_results_spec.rb
@@ -86,6 +86,22 @@ describe Gitlab::SearchResults do
       expect(results.issues_count).to eq 1
     end
 
+    it 'should not list confidential issues for project members with guest role' do
+      project_1.team << [member, :guest]
+      project_2.team << [member, :guest]
+
+      results = described_class.new(member, limit_projects, query)
+      issues = results.objects('issues')
+
+      expect(issues).to include issue
+      expect(issues).not_to include security_issue_1
+      expect(issues).not_to include security_issue_2
+      expect(issues).not_to include security_issue_3
+      expect(issues).not_to include security_issue_4
+      expect(issues).not_to include security_issue_5
+      expect(results.issues_count).to eq 1
+    end
+
     it 'should list confidential issues for author' do
       results = described_class.new(author, limit_projects, query)
       issues = results.objects('issues')
diff --git a/spec/lib/gitlab/sherlock/collection_spec.rb b/spec/lib/gitlab/sherlock/collection_spec.rb
index de6bb86c5ddd8a826487e25e8742d69ffd4e0bc8..2ae79b50e77ca3eb52822f612d96e839d991863b 100644
--- a/spec/lib/gitlab/sherlock/collection_spec.rb
+++ b/spec/lib/gitlab/sherlock/collection_spec.rb
@@ -11,13 +11,13 @@ describe Gitlab::Sherlock::Collection, lib: true do
     it 'adds a new transaction' do
       collection.add(transaction)
 
-      expect(collection).to_not be_empty
+      expect(collection).not_to be_empty
     end
 
     it 'is aliased as <<' do
       collection << transaction
 
-      expect(collection).to_not be_empty
+      expect(collection).not_to be_empty
     end
   end
 
@@ -47,7 +47,7 @@ describe Gitlab::Sherlock::Collection, lib: true do
     it 'returns false for a collection with a transaction' do
       collection.add(transaction)
 
-      expect(collection).to_not be_empty
+      expect(collection).not_to be_empty
     end
   end
 
diff --git a/spec/lib/gitlab/sherlock/query_spec.rb b/spec/lib/gitlab/sherlock/query_spec.rb
index 05da915ccfd827cc693f2512cad2aa1980a0ca5a..0a62042813883267a2952d42897be0ccea68d868 100644
--- a/spec/lib/gitlab/sherlock/query_spec.rb
+++ b/spec/lib/gitlab/sherlock/query_spec.rb
@@ -85,7 +85,7 @@ FROM users;
       frames = query.application_backtrace
 
       expect(frames).to be_an_instance_of(Array)
-      expect(frames).to_not be_empty
+      expect(frames).not_to be_empty
 
       frames.each do |frame|
         expect(frame.path).to start_with(Rails.root.to_s)
diff --git a/spec/lib/gitlab/sherlock/transaction_spec.rb b/spec/lib/gitlab/sherlock/transaction_spec.rb
index 7553f2a045fc6cdd7a1a8b990b90c07b3e748e81..9fe18f253f0bc3dea82f887f6cc7bf1769c87b9a 100644
--- a/spec/lib/gitlab/sherlock/transaction_spec.rb
+++ b/spec/lib/gitlab/sherlock/transaction_spec.rb
@@ -203,7 +203,7 @@ describe Gitlab::Sherlock::Transaction, lib: true do
     end
 
     it 'only tracks queries triggered from the transaction thread' do
-      expect(transaction).to_not receive(:track_query)
+      expect(transaction).not_to receive(:track_query)
 
       Thread.new { subscription.publish('test', time, time, nil, query_data) }.
         join
@@ -226,7 +226,7 @@ describe Gitlab::Sherlock::Transaction, lib: true do
     end
 
     it 'only tracks views rendered from the transaction thread' do
-      expect(transaction).to_not receive(:track_view)
+      expect(transaction).not_to receive(:track_view)
 
       Thread.new { subscription.publish('test', time, time, nil, view_data) }.
         join
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index d940bf05061be03313c5a935161e2f9ca37ab567..c5c1402e8fcfc5d3d02aadb5859e93d395d1e401 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -11,7 +11,7 @@ describe Gitlab::Workhorse, lib: true do
       end
 
       it "raises an error" do
-        expect { subject.send_git_archive(project, "master", "zip") }.to raise_error(RuntimeError)
+        expect { subject.send_git_archive(project.repository, ref: "master", format: "zip") }.to raise_error(RuntimeError)
       end
     end
   end
diff --git a/spec/lib/json_web_token/rsa_token_spec.rb b/spec/lib/json_web_token/rsa_token_spec.rb
index 0c3d3ea7019163dc419e3d51c6285a56c4d98282..1872675451721c6800a5519feb6aa495625f9b76 100644
--- a/spec/lib/json_web_token/rsa_token_spec.rb
+++ b/spec/lib/json_web_token/rsa_token_spec.rb
@@ -23,7 +23,7 @@ describe JSONWebToken::RSAToken do
 
       subject { JWT.decode(rsa_encoded, rsa_key) }
 
-      it { expect{subject}.to_not raise_error }
+      it { expect{subject}.not_to raise_error }
       it { expect(subject.first).to include('key' => 'value') }
       it do
         expect(subject.second).to eq(
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 05cac0212fc28c4458e05ec9b9c12e011d625b16..46fddee7f26d1e9799fbe25d6111c161a62e46c6 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -51,7 +51,7 @@ describe Notify do
 
           context 'when enabled email_author_in_body' do
             before do
-              allow(current_application_settings).to receive(:email_author_in_body).and_return(true)
+              allow_any_instance_of(ApplicationSetting).to receive(:email_author_in_body).and_return(true)
             end
 
             it 'contains a link to note author' do
@@ -231,7 +231,7 @@ describe Notify do
 
           context 'when enabled email_author_in_body' do
             before do
-              allow(current_application_settings).to receive(:email_author_in_body).and_return(true)
+              allow_any_instance_of(ApplicationSetting).to receive(:email_author_in_body).and_return(true)
             end
 
             it 'contains a link to note author' do
@@ -419,26 +419,136 @@ describe Notify do
       end
     end
 
+    describe 'project access requested' do
+      let(:project) { create(:project) }
+      let(:user) { create(:user) }
+      let(:project_member) do
+        project.request_access(user)
+        project.members.request.find_by(user_id: user.id)
+      end
+      subject { Notify.member_access_requested_email('project', project_member.id) }
+
+      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 'contains all the useful information' do
+        is_expected.to have_subject "Request to join the #{project.name_with_namespace} project"
+        is_expected.to have_body_text /#{project.name_with_namespace}/
+        is_expected.to have_body_text /#{namespace_project_project_members_url(project.namespace, project)}/
+        is_expected.to have_body_text /#{project_member.human_access}/
+      end
+    end
+
+    describe 'project access denied' do
+      let(:project) { create(:project) }
+      let(:user) { create(:user) }
+      let(:project_member) do
+        project.request_access(user)
+        project.members.request.find_by(user_id: user.id)
+      end
+      subject { Notify.member_access_denied_email('project', project.id, user.id) }
+
+      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 'contains all the useful information' do
+        is_expected.to have_subject "Access to the #{project.name_with_namespace} project was denied"
+        is_expected.to have_body_text /#{project.name_with_namespace}/
+        is_expected.to have_body_text /#{project.web_url}/
+      end
+    end
+
     describe 'project access changed' do
       let(:project) { create(:project) }
       let(:user) { create(:user) }
       let(:project_member) { create(:project_member, project: project, user: user) }
-      subject { Notify.project_access_granted_email(project_member.id) }
+      subject { Notify.member_access_granted_email('project', project_member.id) }
 
       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/
+      it 'contains all the useful information' do
+        is_expected.to have_subject "Access to the #{project.name_with_namespace} project was granted"
+        is_expected.to have_body_text /#{project.name_with_namespace}/
+        is_expected.to have_body_text /#{project.web_url}/
+        is_expected.to have_body_text /#{project_member.human_access}/
       end
+    end
 
-      it 'contains name of project' do
-        is_expected.to have_body_text /#{project.name}/
-      end
+    def invite_to_project(project:, email:, inviter:)
+      ProjectMember.add_user(project.project_members, 'toto@example.com', Gitlab::Access::DEVELOPER, inviter)
 
-      it 'contains new user role' do
+      project.project_members.invite.last
+    end
+
+    describe 'project invitation' do
+      let(:project) { create(:project) }
+      let(:master) { create(:user).tap { |u| project.team << [u, :master] } }
+      let(:project_member) { invite_to_project(project: project, email: 'toto@example.com', inviter: master) }
+
+      subject { Notify.member_invited_email('project', project_member.id, project_member.invite_token) }
+
+      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 'contains all the useful information' do
+        is_expected.to have_subject "Invitation to join the #{project.name_with_namespace} project"
+        is_expected.to have_body_text /#{project.name_with_namespace}/
+        is_expected.to have_body_text /#{project.web_url}/
         is_expected.to have_body_text /#{project_member.human_access}/
+        is_expected.to have_body_text /#{project_member.invite_token}/
+      end
+    end
+
+    describe 'project invitation accepted' do
+      let(:project) { create(:project) }
+      let(:invited_user) { create(:user) }
+      let(:master) { create(:user).tap { |u| project.team << [u, :master] } }
+      let(:project_member) do
+        invitee = invite_to_project(project: project, email: 'toto@example.com', inviter: master)
+        invitee.accept_invite!(invited_user)
+        invitee
+      end
+
+      subject { Notify.member_invite_accepted_email('project', project_member.id) }
+
+      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 'contains all the useful information' do
+        is_expected.to have_subject 'Invitation accepted'
+        is_expected.to have_body_text /#{project.name_with_namespace}/
+        is_expected.to have_body_text /#{project.web_url}/
+        is_expected.to have_body_text /#{project_member.invite_email}/
+        is_expected.to have_body_text /#{invited_user.name}/
+      end
+    end
+
+    describe 'project invitation declined' do
+      let(:project) { create(:project) }
+      let(:master) { create(:user).tap { |u| project.team << [u, :master] } }
+      let(:project_member) do
+        invitee = invite_to_project(project: project, email: 'toto@example.com', inviter: master)
+        invitee.decline_invite!
+        invitee
+      end
+
+      subject { Notify.member_invite_declined_email('project', project.id, project_member.invite_email, master.id) }
+
+      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 'contains all the useful information' do
+        is_expected.to have_subject 'Invitation declined'
+        is_expected.to have_body_text /#{project.name_with_namespace}/
+        is_expected.to have_body_text /#{project.web_url}/
+        is_expected.to have_body_text /#{project_member.invite_email}/
       end
     end
 
@@ -473,7 +583,7 @@ describe Notify do
 
         context 'when enabled email_author_in_body' do
           before do
-            allow(current_application_settings).to receive(:email_author_in_body).and_return(true)
+            allow_any_instance_of(ApplicationSetting).to receive(:email_author_in_body).and_return(true)
           end
 
           it 'contains a link to note author' do
@@ -554,27 +664,139 @@ describe Notify do
     end
   end
 
-  describe 'group access changed' do
-    let(:group) { create(:group) }
-    let(:user) { create(:user) }
-    let(:membership) { create(:group_member, group: group, user: user) }
+  context 'for a group' do
+    describe 'group access requested' do
+      let(:group) { create(:group) }
+      let(:user) { create(:user) }
+      let(:group_member) do
+        group.request_access(user)
+        group.members.request.find_by(user_id: user.id)
+      end
+      subject { Notify.member_access_requested_email('group', group_member.id) }
 
-    subject { Notify.group_access_granted_email(membership.id) }
+      it_behaves_like 'an email sent from GitLab'
+      it_behaves_like 'it should not have Gmail Actions links'
+      it_behaves_like "a user cannot unsubscribe through footer link"
 
-    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 'contains all the useful information' do
+        is_expected.to have_subject "Request to join the #{group.name} group"
+        is_expected.to have_body_text /#{group.name}/
+        is_expected.to have_body_text /#{group_group_members_url(group)}/
+        is_expected.to have_body_text /#{group_member.human_access}/
+      end
+    end
 
-    it 'has the correct subject' do
-      is_expected.to have_subject /Access to group was granted/
+    describe 'group access denied' do
+      let(:group) { create(:group) }
+      let(:user) { create(:user) }
+      let(:group_member) do
+        group.request_access(user)
+        group.members.request.find_by(user_id: user.id)
+      end
+      subject { Notify.member_access_denied_email('group', group.id, user.id) }
+
+      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 'contains all the useful information' do
+        is_expected.to have_subject "Access to the #{group.name} group was denied"
+        is_expected.to have_body_text /#{group.name}/
+        is_expected.to have_body_text /#{group.web_url}/
+      end
+    end
+
+    describe 'group access changed' do
+      let(:group) { create(:group) }
+      let(:user) { create(:user) }
+      let(:group_member) { create(:group_member, group: group, user: user) }
+
+      subject { Notify.member_access_granted_email('group', group_member.id) }
+
+      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 'contains all the useful information' do
+        is_expected.to have_subject "Access to the #{group.name} group was granted"
+        is_expected.to have_body_text /#{group.name}/
+        is_expected.to have_body_text /#{group.web_url}/
+        is_expected.to have_body_text /#{group_member.human_access}/
+      end
+    end
+
+    def invite_to_group(group:, email:, inviter:)
+      GroupMember.add_user(group.group_members, 'toto@example.com', Gitlab::Access::DEVELOPER, inviter)
+
+      group.group_members.invite.last
     end
 
-    it 'contains name of project' do
-      is_expected.to have_body_text /#{group.name}/
+    describe 'group invitation' do
+      let(:group) { create(:group) }
+      let(:owner) { create(:user).tap { |u| group.add_user(u, Gitlab::Access::OWNER) } }
+      let(:group_member) { invite_to_group(group: group, email: 'toto@example.com', inviter: owner) }
+
+      subject { Notify.member_invited_email('group', group_member.id, group_member.invite_token) }
+
+      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 'contains all the useful information' do
+        is_expected.to have_subject "Invitation to join the #{group.name} group"
+        is_expected.to have_body_text /#{group.name}/
+        is_expected.to have_body_text /#{group.web_url}/
+        is_expected.to have_body_text /#{group_member.human_access}/
+        is_expected.to have_body_text /#{group_member.invite_token}/
+      end
     end
 
-    it 'contains new user role' do
-      is_expected.to have_body_text /#{membership.human_access}/
+    describe 'group invitation accepted' do
+      let(:group) { create(:group) }
+      let(:invited_user) { create(:user) }
+      let(:owner) { create(:user).tap { |u| group.add_user(u, Gitlab::Access::OWNER) } }
+      let(:group_member) do
+        invitee = invite_to_group(group: group, email: 'toto@example.com', inviter: owner)
+        invitee.accept_invite!(invited_user)
+        invitee
+      end
+
+      subject { Notify.member_invite_accepted_email('group', group_member.id) }
+
+      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 'contains all the useful information' do
+        is_expected.to have_subject 'Invitation accepted'
+        is_expected.to have_body_text /#{group.name}/
+        is_expected.to have_body_text /#{group.web_url}/
+        is_expected.to have_body_text /#{group_member.invite_email}/
+        is_expected.to have_body_text /#{invited_user.name}/
+      end
+    end
+
+    describe 'group invitation declined' do
+      let(:group) { create(:group) }
+      let(:owner) { create(:user).tap { |u| group.add_user(u, Gitlab::Access::OWNER) } }
+      let(:group_member) do
+        invitee = invite_to_group(group: group, email: 'toto@example.com', inviter: owner)
+        invitee.decline_invite!
+        invitee
+      end
+
+      subject { Notify.member_invite_declined_email('group', group.id, group_member.invite_email, owner.id) }
+
+      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 'contains all the useful information' do
+        is_expected.to have_subject 'Invitation declined'
+        is_expected.to have_body_text /#{group.name}/
+        is_expected.to have_body_text /#{group.web_url}/
+        is_expected.to have_body_text /#{group_member.invite_email}/
+      end
     end
   end
 
diff --git a/spec/mailers/previews/devise_mailer_preview.rb b/spec/mailers/previews/devise_mailer_preview.rb
new file mode 100644
index 0000000000000000000000000000000000000000..dc3062a43327c20e1ba3b8234dcf746d9302db4b
--- /dev/null
+++ b/spec/mailers/previews/devise_mailer_preview.rb
@@ -0,0 +1,11 @@
+class DeviseMailerPreview < ActionMailer::Preview
+  def confirmation_instructions_for_signup
+    user = User.new(name: 'Jane Doe', email: 'signup@example.com')
+    DeviseMailer.confirmation_instructions(user, 'faketoken', {})
+  end
+
+  def confirmation_instructions_for_new_email
+    user = User.last
+    DeviseMailer.confirmation_instructions(user, 'faketoken', {})
+  end
+end
diff --git a/spec/mailers/shared/notify.rb b/spec/mailers/shared/notify.rb
index 5a85cb501dd27642d4aa364d4972cdf774bddd92..93de5850ba28aa0cef1014a7102e9fe41d122ae4 100644
--- a/spec/mailers/shared/notify.rb
+++ b/spec/mailers/shared/notify.rb
@@ -146,8 +146,8 @@ shared_examples 'it should have Gmail Actions links' do
 end
 
 shared_examples 'it should not have Gmail Actions links' do
-  it { is_expected.to_not have_body_text '<script type="application/ld+json">' }
-  it { is_expected.to_not have_body_text /ViewAction/ }
+  it { is_expected.not_to have_body_text '<script type="application/ld+json">' }
+  it { is_expected.not_to have_body_text /ViewAction/ }
 end
 
 shared_examples 'it should show Gmail Actions View Issue link' do
diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1acb5846fcf9028026ddb5104637bfb58fc791e7
--- /dev/null
+++ b/spec/models/ability_spec.rb
@@ -0,0 +1,117 @@
+require 'spec_helper'
+
+describe Ability, lib: true do
+  describe '.users_that_can_read_project' do
+    context 'using a public project' do
+      it 'returns all the users' do
+        project = create(:project, :public)
+        user = build(:user)
+
+        expect(described_class.users_that_can_read_project([user], project)).
+          to eq([user])
+      end
+    end
+
+    context 'using an internal project' do
+      let(:project) { create(:project, :internal) }
+
+      it 'returns users that are administrators' do
+        user = build(:user, admin: true)
+
+        expect(described_class.users_that_can_read_project([user], project)).
+          to eq([user])
+      end
+
+      it 'returns internal users while skipping external users' do
+        user1 = build(:user)
+        user2 = build(:user, external: true)
+        users = [user1, user2]
+
+        expect(described_class.users_that_can_read_project(users, project)).
+          to eq([user1])
+      end
+
+      it 'returns external users if they are the project owner' do
+        user1 = build(:user, external: true)
+        user2 = build(:user, external: true)
+        users = [user1, user2]
+
+        expect(project).to receive(:owner).twice.and_return(user1)
+
+        expect(described_class.users_that_can_read_project(users, project)).
+          to eq([user1])
+      end
+
+      it 'returns external users if they are project members' do
+        user1 = build(:user, external: true)
+        user2 = build(:user, external: true)
+        users = [user1, user2]
+
+        expect(project.team).to receive(:members).twice.and_return([user1])
+
+        expect(described_class.users_that_can_read_project(users, project)).
+          to eq([user1])
+      end
+
+      it 'returns an empty Array if all users are external users without access' do
+        user1 = build(:user, external: true)
+        user2 = build(:user, external: true)
+        users = [user1, user2]
+
+        expect(described_class.users_that_can_read_project(users, project)).
+          to eq([])
+      end
+    end
+
+    context 'using a private project' do
+      let(:project) { create(:project, :private) }
+
+      it 'returns users that are administrators' do
+        user = build(:user, admin: true)
+
+        expect(described_class.users_that_can_read_project([user], project)).
+          to eq([user])
+      end
+
+      it 'returns external users if they are the project owner' do
+        user1 = build(:user, external: true)
+        user2 = build(:user, external: true)
+        users = [user1, user2]
+
+        expect(project).to receive(:owner).twice.and_return(user1)
+
+        expect(described_class.users_that_can_read_project(users, project)).
+          to eq([user1])
+      end
+
+      it 'returns external users if they are project members' do
+        user1 = build(:user, external: true)
+        user2 = build(:user, external: true)
+        users = [user1, user2]
+
+        expect(project.team).to receive(:members).twice.and_return([user1])
+
+        expect(described_class.users_that_can_read_project(users, project)).
+          to eq([user1])
+      end
+
+      it 'returns an empty Array if all users are internal users without access' do
+        user1 = build(:user)
+        user2 = build(:user)
+        users = [user1, user2]
+
+        expect(described_class.users_that_can_read_project(users, project)).
+          to eq([])
+      end
+
+      it 'returns an empty Array if all users are external users without access' do
+        user1 = build(:user, external: true)
+        user2 = build(:user, external: true)
+        users = [user1, user2]
+
+        expect(described_class.users_that_can_read_project(users, project)).
+          to eq([])
+      end
+    end
+  end
+end
diff --git a/spec/models/award_emoji_spec.rb b/spec/models/award_emoji_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cb3c592f8cd24182fe8999e7a406f4f2250d10ee
--- /dev/null
+++ b/spec/models/award_emoji_spec.rb
@@ -0,0 +1,30 @@
+require 'spec_helper'
+
+describe AwardEmoji, models: true do
+  describe 'Associations' do
+    it { is_expected.to belong_to(:awardable) }
+    it { is_expected.to belong_to(:user) }
+  end
+
+  describe 'modules' do
+    it { is_expected.to include_module(Participable) }
+  end
+
+  describe "validations" do
+    it { is_expected.to validate_presence_of(:awardable) }
+    it { is_expected.to validate_presence_of(:user) }
+    it { is_expected.to validate_presence_of(:name) }
+
+    # To circumvent a bug in the shoulda matchers
+    describe "scoped uniqueness validation" do
+      it "rejects duplicate award emoji" do
+        user  = create(:user)
+        issue = create(:issue)
+        create(:award_emoji, user: user, awardable: issue)
+        new_award = build(:award_emoji, user: user, awardable: issue)
+
+        expect(new_award).not_to be_valid
+      end
+    end
+  end
+end
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index abae3271a5c860d26bc597a3fb8beccba3c6977a..5d1fa8226e56346fe577a7e859f0141d6fb56e96 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -1,18 +1,17 @@
 require 'spec_helper'
 
 describe Ci::Build, models: true do
-  let(:project) { FactoryGirl.create :project }
-  let(:commit) { FactoryGirl.create :ci_commit, project: project }
-  let(:build) { FactoryGirl.create :ci_build, commit: commit }
+  let(:project) { create(:project) }
+  let(:pipeline) { create(:ci_pipeline, project: project) }
+  let(:build) { create(:ci_build, pipeline: pipeline) }
 
   it { is_expected.to validate_presence_of :ref }
 
   it { is_expected.to respond_to :trace_html }
 
   describe '#first_pending' do
-    let(:first) { FactoryGirl.create :ci_build, commit: commit, status: 'pending', created_at: Date.yesterday }
-    let(:second) { FactoryGirl.create :ci_build, commit: commit, status: 'pending' }
-    before { first; second }
+    let!(:first) { create(:ci_build, pipeline: pipeline, status: 'pending', created_at: Date.yesterday) }
+    let!(:second) { create(:ci_build, pipeline: pipeline, status: 'pending') }
     subject { Ci::Build.first_pending }
 
     it { is_expected.to be_a(Ci::Build) }
@@ -90,7 +89,7 @@ describe Ci::Build, models: true do
         build.update_attributes(trace: token)
       end
 
-      it { is_expected.to_not include(token) }
+      it { is_expected.not_to include(token) }
     end
   end
 
@@ -98,7 +97,7 @@ describe Ci::Build, models: true do
   # describe :timeout do
   #   subject { build.timeout }
   #
-  #   it { is_expected.to eq(commit.project.timeout) }
+  #   it { is_expected.to eq(pipeline.project.timeout) }
   # end
 
   describe '#options' do
@@ -125,13 +124,13 @@ describe Ci::Build, models: true do
   describe '#project' do
     subject { build.project }
 
-    it { is_expected.to eq(commit.project) }
+    it { is_expected.to eq(pipeline.project) }
   end
 
   describe '#project_id' do
     subject { build.project_id }
 
-    it { is_expected.to eq(commit.project_id) }
+    it { is_expected.to eq(pipeline.project_id) }
   end
 
   describe '#project_name' do
@@ -219,8 +218,8 @@ describe Ci::Build, models: true do
         it { is_expected.to eq(predefined_variables + yaml_variables + secure_variables) }
 
         context 'and trigger variables' do
-          let(:trigger) { FactoryGirl.create :ci_trigger, project: project }
-          let(:trigger_request) { FactoryGirl.create :ci_trigger_request_with_variables, commit: commit, trigger: trigger }
+          let(:trigger) { create(:ci_trigger, project: project) }
+          let(:trigger_request) { create(:ci_trigger_request_with_variables, pipeline: pipeline, trigger: trigger) }
           let(:trigger_variables) do
             [
               { key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false }
@@ -317,7 +316,7 @@ describe Ci::Build, models: true do
 
     context 'when build does not have tags' do
       subject { create(:ci_build, tag_list: []) }
-      it { is_expected.to_not have_tags }
+      it { is_expected.not_to have_tags }
     end
   end
 
@@ -329,7 +328,7 @@ describe Ci::Build, models: true do
     end
 
     context 'if there are runner' do
-      let(:runner) { FactoryGirl.create :ci_runner }
+      let(:runner) { create(:ci_runner) }
 
       before do
         build.project.runners << runner
@@ -366,7 +365,7 @@ describe Ci::Build, models: true do
         it { is_expected.to be_truthy }
 
         context "and there are specific runner" do
-          let(:runner) { FactoryGirl.create :ci_runner, contacted_at: 1.second.ago }
+          let(:runner) { create(:ci_runner, contacted_at: 1.second.ago) }
 
           before do
             build.project.runners << runner
@@ -398,9 +397,34 @@ describe Ci::Build, models: true do
     context 'artifacts archive exists' do
       let(:build) { create(:ci_build, :artifacts) }
       it { is_expected.to be_truthy }
+
+      context 'is expired' do
+        before { build.update(artifacts_expire_at: Time.now - 7.days)  }
+        it { is_expected.to be_falsy }
+      end
+
+      context 'is not expired' do
+        before { build.update(artifacts_expire_at: Time.now + 7.days)  }
+        it { is_expected.to be_truthy }
+      end
     end
   end
 
+  describe '#artifacts_expired?' do
+    subject { build.artifacts_expired? }
+
+    context 'is expired' do
+      before { build.update(artifacts_expire_at: Time.now - 7.days)  }
+
+      it { is_expected.to be_truthy }
+    end
+
+    context 'is not expired' do
+      before { build.update(artifacts_expire_at: Time.now + 7.days)  }
+
+      it { is_expected.to be_falsey }
+    end
+  end
 
   describe '#artifacts_metadata?' do
     subject { build.artifacts_metadata? }
@@ -413,9 +437,8 @@ describe Ci::Build, models: true do
       it { is_expected.to be_truthy }
     end
   end
-
   describe '#repo_url' do
-    let(:build) { FactoryGirl.create :ci_build }
+    let(:build) { create(:ci_build) }
     let(:project) { build.project }
 
     subject { build.repo_url }
@@ -428,11 +451,55 @@ describe Ci::Build, models: true do
     it { is_expected.to include(project.web_url[7..-1]) }
   end
 
+  describe '#artifacts_expire_in' do
+    subject { build.artifacts_expire_in }
+    it { is_expected.to be_nil }
+
+    context 'when artifacts_expire_at is specified' do
+      let(:expire_at) { Time.now + 7.days }
+
+      before { build.artifacts_expire_at = expire_at }
+
+      it { is_expected.to be_within(5).of(expire_at - Time.now) }
+    end
+  end
+
+  describe '#artifacts_expire_in=' do
+    subject { build.artifacts_expire_in }
+
+    it 'when assigning valid duration' do
+      build.artifacts_expire_in = '7 days'
+
+      is_expected.to be_within(10).of(7.days.to_i)
+    end
+
+    it 'when assigning invalid duration' do
+      expect { build.artifacts_expire_in = '7 elephants' }.to raise_error(ChronicDuration::DurationParseError)
+      is_expected.to be_nil
+    end
+
+    it 'when resseting value' do
+      build.artifacts_expire_in = nil
+
+      is_expected.to be_nil
+    end
+  end
+
+  describe '#keep_artifacts!' do
+    let(:build) { create(:ci_build, artifacts_expire_at: Time.now + 7.days) }
+
+    it 'to reset expire_at' do
+      build.keep_artifacts!
+
+      expect(build.artifacts_expire_at).to be_nil
+    end
+  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' }
+    let!(:build) { create(:ci_build, pipeline: pipeline, name: 'build', stage_idx: 0, stage: 'build') }
+    let!(:rspec_test) { create(:ci_build, pipeline: pipeline, name: 'rspec', stage_idx: 1, stage: 'test') }
+    let!(:rubocop_test) { create(:ci_build, pipeline: pipeline, name: 'rubocop', stage_idx: 1, stage: 'test') }
+    let!(:staging) { create(:ci_build, pipeline: pipeline, 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
@@ -452,20 +519,19 @@ describe Ci::Build, models: true do
     end
   end
 
-  def create_mr(build, commit, factory: :merge_request, created_at: Time.now)
-    FactoryGirl.create(factory,
-                       source_project_id: commit.gl_project_id,
-                       target_project_id: commit.gl_project_id,
-                       source_branch: build.ref,
-                       created_at: created_at)
+  def create_mr(build, pipeline, factory: :merge_request, created_at: Time.now)
+    create(factory, source_project_id: pipeline.gl_project_id,
+                    target_project_id: pipeline.gl_project_id,
+                    source_branch: build.ref,
+                    created_at: created_at)
   end
 
   describe '#merge_request' do
-    context 'when a MR has a reference to the commit' do
+    context 'when a MR has a reference to the pipeline' do
       before do
-        @merge_request = create_mr(build, commit, factory: :merge_request)
+        @merge_request = create_mr(build, pipeline, factory: :merge_request)
 
-        commits = [double(id: commit.sha)]
+        commits = [double(id: pipeline.sha)]
         allow(@merge_request).to receive(:commits).and_return(commits)
         allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request])
       end
@@ -475,19 +541,19 @@ describe Ci::Build, models: true do
       end
     end
 
-    context 'when there is not a MR referencing the commit' do
+    context 'when there is not a MR referencing the pipeline' do
       it 'returns nil' do
         expect(build.merge_request).to be_nil
       end
     end
 
-    context 'when more than one MR have a reference to the commit' do
+    context 'when more than one MR have a reference to the pipeline' do
       before do
-        @merge_request = create_mr(build, commit, factory: :merge_request)
+        @merge_request = create_mr(build, pipeline, factory: :merge_request)
         @merge_request.close!
-        @merge_request2 = create_mr(build, commit, factory: :merge_request)
+        @merge_request2 = create_mr(build, pipeline, factory: :merge_request)
 
-        commits = [double(id: commit.sha)]
+        commits = [double(id: pipeline.sha)]
         allow(@merge_request).to receive(:commits).and_return(commits)
         allow(@merge_request2).to receive(:commits).and_return(commits)
         allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request, @merge_request2])
@@ -500,11 +566,11 @@ describe Ci::Build, models: true do
 
     context 'when a Build is created after the MR' do
       before do
-        @merge_request = create_mr(build, commit, factory: :merge_request_with_diffs)
-        commit2 = FactoryGirl.create :ci_commit, project: project
-        @build2 = FactoryGirl.create :ci_build, commit: commit2
+        @merge_request = create_mr(build, pipeline, factory: :merge_request_with_diffs)
+        pipeline2 = create(:ci_pipeline, project: project)
+        @build2 = create(:ci_build, pipeline: pipeline2)
 
-        commits = [double(id: commit.sha), double(id: commit2.sha)]
+        commits = [double(id: pipeline.sha), double(id: pipeline2.sha)]
         allow(@merge_request).to receive(:commits).and_return(commits)
         allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request])
       end
@@ -534,7 +600,7 @@ describe Ci::Build, models: true do
       end
 
       it 'should set erase date' do
-        expect(build.erased_at).to_not be_falsy
+        expect(build.erased_at).not_to be_falsy
       end
     end
 
@@ -606,7 +672,7 @@ describe Ci::Build, models: true do
 
         describe '#erase' do
           it 'should not raise error' do
-            expect { build.erase }.to_not raise_error
+            expect { build.erase }.not_to raise_error
           end
         end
       end
diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb
deleted file mode 100644
index 1b5940ad5a84fa309938cd7e4c6f48884bfd7b96..0000000000000000000000000000000000000000
--- a/spec/models/ci/commit_spec.rb
+++ /dev/null
@@ -1,403 +0,0 @@
-require 'spec_helper'
-
-describe Ci::Commit, models: true do
-  let(:project) { FactoryGirl.create :empty_project }
-  let(:commit) { FactoryGirl.create :ci_commit, project: project }
-
-  it { is_expected.to belong_to(:project) }
-  it { is_expected.to have_many(:statuses) }
-  it { is_expected.to have_many(:trigger_requests) }
-  it { is_expected.to have_many(:builds) }
-  it { is_expected.to validate_presence_of :sha }
-  it { is_expected.to validate_presence_of :status }
-
-  it { is_expected.to respond_to :git_author_name }
-  it { is_expected.to respond_to :git_author_email }
-  it { is_expected.to respond_to :short_sha }
-
-  describe :valid_commit_sha do
-    context 'commit.sha can not start with 00000000' do
-      before do
-        commit.sha = '0' * 40
-        commit.valid_commit_sha
-      end
-
-      it('commit errors should not be empty') { expect(commit.errors).not_to be_empty }
-    end
-  end
-
-  describe :short_sha do
-    subject { commit.short_sha }
-
-    it 'has 8 items' do
-      expect(subject.size).to eq(8)
-    end
-    it { expect(commit.sha).to start_with(subject) }
-  end
-
-  describe :create_next_builds do
-  end
-
-  describe :retried do
-    subject { commit.retried }
-
-    before do
-      @commit1 = FactoryGirl.create :ci_build, commit: commit, name: 'deploy'
-      @commit2 = FactoryGirl.create :ci_build, commit: commit, name: 'deploy'
-    end
-
-    it 'returns old builds' do
-      is_expected.to contain_exactly(@commit1)
-    end
-  end
-
-  describe :create_builds do
-    let!(:commit) { FactoryGirl.create :ci_commit, project: project, ref: 'master', tag: false }
-
-    def create_builds(trigger_request = nil)
-      commit.create_builds(nil, trigger_request)
-    end
-
-    def create_next_builds
-      commit.create_next_builds(commit.builds.order(:id).last)
-    end
-
-    it 'creates builds' do
-      expect(create_builds).to be_truthy
-      commit.builds.update_all(status: "success")
-      expect(commit.builds.count(:all)).to eq(2)
-
-      expect(create_next_builds).to be_truthy
-      commit.builds.update_all(status: "success")
-      expect(commit.builds.count(:all)).to eq(4)
-
-      expect(create_next_builds).to be_truthy
-      commit.builds.update_all(status: "success")
-      expect(commit.builds.count(:all)).to eq(5)
-
-      expect(create_next_builds).to be_falsey
-    end
-
-    context 'custom stage with first job allowed to fail' do
-      let(:yaml) do
-        {
-          stages: ['clean', 'test'],
-          clean_job: {
-            stage: 'clean',
-            allow_failure: true,
-            script: 'BUILD',
-          },
-          test_job: {
-            stage: 'test',
-            script: 'TEST',
-          },
-        }
-      end
-
-      before do
-        stub_ci_commit_yaml_file(YAML.dump(yaml))
-        create_builds
-      end
-
-      it 'properly schedules builds' do
-        expect(commit.builds.pluck(:status)).to contain_exactly('pending')
-        commit.builds.running_or_pending.each(&:drop)
-        expect(commit.builds.pluck(:status)).to contain_exactly('pending', 'failed')
-      end
-    end
-
-    context 'properly creates builds when "when" is defined' do
-      let(:yaml) do
-        {
-          stages: ["build", "test", "test_failure", "deploy", "cleanup"],
-          build: {
-            stage: "build",
-            script: "BUILD",
-          },
-          test: {
-            stage: "test",
-            script: "TEST",
-          },
-          test_failure: {
-            stage: "test_failure",
-            script: "ON test failure",
-            when: "on_failure",
-          },
-          deploy: {
-            stage: "deploy",
-            script: "PUBLISH",
-          },
-          cleanup: {
-            stage: "cleanup",
-            script: "TIDY UP",
-            when: "always",
-          }
-        }
-      end
-
-      before do
-        stub_ci_commit_yaml_file(YAML.dump(yaml))
-      end
-
-      context 'when builds are successful' do
-        it 'properly creates builds' do
-          expect(create_builds).to be_truthy
-          expect(commit.builds.pluck(:name)).to contain_exactly('build')
-          expect(commit.builds.pluck(:status)).to contain_exactly('pending')
-          commit.builds.running_or_pending.each(&:success)
-
-          expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test')
-          expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending')
-          commit.builds.running_or_pending.each(&:success)
-
-          expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy')
-          expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'pending')
-          commit.builds.running_or_pending.each(&:success)
-
-          expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy', 'cleanup')
-          expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'success', 'pending')
-          commit.builds.running_or_pending.each(&:success)
-
-          expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'success', 'success')
-          commit.reload
-          expect(commit.status).to eq('success')
-        end
-      end
-
-      context 'when test job fails' do
-        it 'properly creates builds' do
-          expect(create_builds).to be_truthy
-          expect(commit.builds.pluck(:name)).to contain_exactly('build')
-          expect(commit.builds.pluck(:status)).to contain_exactly('pending')
-          commit.builds.running_or_pending.each(&:success)
-
-          expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test')
-          expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending')
-          commit.builds.running_or_pending.each(&:drop)
-
-          expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure')
-          expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'pending')
-          commit.builds.running_or_pending.each(&:success)
-
-          expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup')
-          expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'success', 'pending')
-          commit.builds.running_or_pending.each(&:success)
-
-          expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'success', 'success')
-          commit.reload
-          expect(commit.status).to eq('failed')
-        end
-      end
-
-      context 'when test and test_failure jobs fail' do
-        it 'properly creates builds' do
-          expect(create_builds).to be_truthy
-          expect(commit.builds.pluck(:name)).to contain_exactly('build')
-          expect(commit.builds.pluck(:status)).to contain_exactly('pending')
-          commit.builds.running_or_pending.each(&:success)
-
-          expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test')
-          expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending')
-          commit.builds.running_or_pending.each(&:drop)
-
-          expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure')
-          expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'pending')
-          commit.builds.running_or_pending.each(&:drop)
-
-          expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup')
-          expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'failed', 'pending')
-          commit.builds.running_or_pending.each(&:success)
-
-          expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup')
-          expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'failed', 'success')
-          commit.reload
-          expect(commit.status).to eq('failed')
-        end
-      end
-
-      context 'when deploy job fails' do
-        it 'properly creates builds' do
-          expect(create_builds).to be_truthy
-          expect(commit.builds.pluck(:name)).to contain_exactly('build')
-          expect(commit.builds.pluck(:status)).to contain_exactly('pending')
-          commit.builds.running_or_pending.each(&:success)
-
-          expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test')
-          expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending')
-          commit.builds.running_or_pending.each(&:success)
-
-          expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy')
-          expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'pending')
-          commit.builds.running_or_pending.each(&:drop)
-
-          expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy', 'cleanup')
-          expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'failed', 'pending')
-          commit.builds.running_or_pending.each(&:success)
-
-          expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'failed', 'success')
-          commit.reload
-          expect(commit.status).to eq('failed')
-        end
-      end
-
-      context 'when build is canceled in the second stage' do
-        it 'does not schedule builds after build has been canceled' do
-          expect(create_builds).to be_truthy
-          expect(commit.builds.pluck(:name)).to contain_exactly('build')
-          expect(commit.builds.pluck(:status)).to contain_exactly('pending')
-          commit.builds.running_or_pending.each(&:success)
-
-          expect(commit.builds.running_or_pending).to_not be_empty
-
-          expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test')
-          expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending')
-          commit.builds.running_or_pending.each(&:cancel)
-
-          expect(commit.builds.running_or_pending).to be_empty
-          expect(commit.reload.status).to eq('canceled')
-        end
-      end
-    end
-  end
-
-  describe "#finished_at" do
-    let(:commit) { FactoryGirl.create :ci_commit }
-
-    it "returns finished_at of latest build" do
-      build = FactoryGirl.create :ci_build, commit: commit, finished_at: Time.now - 60
-      FactoryGirl.create :ci_build, commit: commit, finished_at: Time.now - 120
-
-      expect(commit.finished_at.to_i).to eq(build.finished_at.to_i)
-    end
-
-    it "returns nil if there is no finished build" do
-      FactoryGirl.create :ci_not_started_build, commit: commit
-
-      expect(commit.finished_at).to be_nil
-    end
-  end
-
-  describe "coverage" do
-    let(:project) { FactoryGirl.create :empty_project, build_coverage_regex: "/.*/" }
-    let(:commit) { FactoryGirl.create :ci_commit, project: project }
-
-    it "calculates average when there are two builds with coverage" do
-      FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit
-      FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, commit: commit
-      expect(commit.coverage).to eq("35.00")
-    end
-
-    it "calculates average when there are two builds with coverage and one with nil" do
-      FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit
-      FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, commit: commit
-      FactoryGirl.create :ci_build, commit: commit
-      expect(commit.coverage).to eq("35.00")
-    end
-
-    it "calculates average when there are two builds with coverage and one is retried" do
-      FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit
-      FactoryGirl.create :ci_build, name: "rubocop", coverage: 30, commit: commit
-      FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, commit: commit
-      expect(commit.coverage).to eq("35.00")
-    end
-
-    it "calculates average when there is one build without coverage" do
-      FactoryGirl.create :ci_build, commit: commit
-      expect(commit.coverage).to be_nil
-    end
-  end
-
-  describe '#retryable?' do
-    subject { commit.retryable? }
-
-    context 'no failed builds' do
-      before do
-        FactoryGirl.create :ci_build, name: "rspec", commit: commit, status: 'success'
-      end
-
-      it 'be not retryable' do
-        is_expected.to be_falsey
-      end
-    end
-
-    context 'with failed builds' do
-      before do
-        FactoryGirl.create :ci_build, name: "rspec", commit: commit, status: 'running'
-        FactoryGirl.create :ci_build, name: "rubocop", commit: commit, status: 'failed'
-      end
-
-      it 'be retryable' do
-        is_expected.to be_truthy
-      end
-    end
-  end
-
-  describe '#stages' do
-    let(:commit2) { FactoryGirl.create :ci_commit, project: project }
-    subject { CommitStatus.where(commit: [commit, commit2]).stages }
-
-    before do
-      FactoryGirl.create :ci_build, commit: commit2, stage: 'test', stage_idx: 1
-      FactoryGirl.create :ci_build, commit: commit, stage: 'build', stage_idx: 0
-    end
-
-    it 'return all stages' do
-      is_expected.to eq(%w(build test))
-    end
-  end
-
-  describe '#update_state' do
-    it 'execute update_state after touching object' do
-      expect(commit).to receive(:update_state).and_return(true)
-      commit.touch
-    end
-
-    context 'dependent objects' do
-      let(:commit_status) { build :commit_status, commit: commit }
-
-      it 'execute update_state after saving dependent object' do
-        expect(commit).to receive(:update_state).and_return(true)
-        commit_status.save
-      end
-    end
-
-    context 'update state' do
-      let(:current) { Time.now.change(usec: 0) }
-      let(:build) { FactoryGirl.create :ci_build, :success, commit: commit, started_at: current - 120, finished_at: current - 60 }
-
-      before do
-        build
-      end
-
-      [:status, :started_at, :finished_at, :duration].each do |param|
-        it "update #{param}" do
-          expect(commit.send(param)).to eq(build.send(param))
-        end
-      end
-    end
-  end
-
-  describe '#branch?' do
-    subject { commit.branch? }
-
-    context 'is not a tag' do
-      before do
-        commit.tag = false
-      end
-
-      it 'return true when tag is set to false' do
-        is_expected.to be_truthy
-      end
-    end
-
-    context 'is not a tag' do
-      before do
-        commit.tag = true
-      end
-
-      it 'return false when tag is set to true' do
-        is_expected.to be_falsey
-      end
-    end
-  end
-end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0d769ed7324c03ce26b2a4389e2522370a92f310
--- /dev/null
+++ b/spec/models/ci/pipeline_spec.rb
@@ -0,0 +1,403 @@
+require 'spec_helper'
+
+describe Ci::Pipeline, models: true do
+  let(:project) { FactoryGirl.create :empty_project }
+  let(:pipeline) { FactoryGirl.create :ci_pipeline, project: project }
+
+  it { is_expected.to belong_to(:project) }
+  it { is_expected.to have_many(:statuses) }
+  it { is_expected.to have_many(:trigger_requests) }
+  it { is_expected.to have_many(:builds) }
+  it { is_expected.to validate_presence_of :sha }
+  it { is_expected.to validate_presence_of :status }
+
+  it { is_expected.to respond_to :git_author_name }
+  it { is_expected.to respond_to :git_author_email }
+  it { is_expected.to respond_to :short_sha }
+
+  describe :valid_commit_sha do
+    context 'commit.sha can not start with 00000000' do
+      before do
+        pipeline.sha = '0' * 40
+        pipeline.valid_commit_sha
+      end
+
+      it('commit errors should not be empty') { expect(pipeline.errors).not_to be_empty }
+    end
+  end
+
+  describe :short_sha do
+    subject { pipeline.short_sha }
+
+    it 'has 8 items' do
+      expect(subject.size).to eq(8)
+    end
+    it { expect(pipeline.sha).to start_with(subject) }
+  end
+
+  describe :create_next_builds do
+  end
+
+  describe :retried do
+    subject { pipeline.retried }
+
+    before do
+      @build1 = FactoryGirl.create :ci_build, pipeline: pipeline, name: 'deploy'
+      @build2 = FactoryGirl.create :ci_build, pipeline: pipeline, name: 'deploy'
+    end
+
+    it 'returns old builds' do
+      is_expected.to contain_exactly(@build1)
+    end
+  end
+
+  describe :create_builds do
+    let!(:pipeline) { FactoryGirl.create :ci_pipeline, project: project, ref: 'master', tag: false }
+
+    def create_builds(trigger_request = nil)
+      pipeline.create_builds(nil, trigger_request)
+    end
+
+    def create_next_builds
+      pipeline.create_next_builds(pipeline.builds.order(:id).last)
+    end
+
+    it 'creates builds' do
+      expect(create_builds).to be_truthy
+      pipeline.builds.update_all(status: "success")
+      expect(pipeline.builds.count(:all)).to eq(2)
+
+      expect(create_next_builds).to be_truthy
+      pipeline.builds.update_all(status: "success")
+      expect(pipeline.builds.count(:all)).to eq(4)
+
+      expect(create_next_builds).to be_truthy
+      pipeline.builds.update_all(status: "success")
+      expect(pipeline.builds.count(:all)).to eq(5)
+
+      expect(create_next_builds).to be_falsey
+    end
+
+    context 'custom stage with first job allowed to fail' do
+      let(:yaml) do
+        {
+          stages: ['clean', 'test'],
+          clean_job: {
+            stage: 'clean',
+            allow_failure: true,
+            script: 'BUILD',
+          },
+          test_job: {
+            stage: 'test',
+            script: 'TEST',
+          },
+        }
+      end
+
+      before do
+        stub_ci_pipeline_yaml_file(YAML.dump(yaml))
+        create_builds
+      end
+
+      it 'properly schedules builds' do
+        expect(pipeline.builds.pluck(:status)).to contain_exactly('pending')
+        pipeline.builds.running_or_pending.each(&:drop)
+        expect(pipeline.builds.pluck(:status)).to contain_exactly('pending', 'failed')
+      end
+    end
+
+    context 'properly creates builds when "when" is defined' do
+      let(:yaml) do
+        {
+          stages: ["build", "test", "test_failure", "deploy", "cleanup"],
+          build: {
+            stage: "build",
+            script: "BUILD",
+          },
+          test: {
+            stage: "test",
+            script: "TEST",
+          },
+          test_failure: {
+            stage: "test_failure",
+            script: "ON test failure",
+            when: "on_failure",
+          },
+          deploy: {
+            stage: "deploy",
+            script: "PUBLISH",
+          },
+          cleanup: {
+            stage: "cleanup",
+            script: "TIDY UP",
+            when: "always",
+          }
+        }
+      end
+
+      before do
+        stub_ci_pipeline_yaml_file(YAML.dump(yaml))
+      end
+
+      context 'when builds are successful' do
+        it 'properly creates builds' do
+          expect(create_builds).to be_truthy
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('pending')
+          pipeline.builds.running_or_pending.each(&:success)
+
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'pending')
+          pipeline.builds.running_or_pending.each(&:success)
+
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'success', 'pending')
+          pipeline.builds.running_or_pending.each(&:success)
+
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy', 'cleanup')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'success', 'success', 'pending')
+          pipeline.builds.running_or_pending.each(&:success)
+
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'success', 'success', 'success')
+          pipeline.reload
+          expect(pipeline.status).to eq('success')
+        end
+      end
+
+      context 'when test job fails' do
+        it 'properly creates builds' do
+          expect(create_builds).to be_truthy
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('pending')
+          pipeline.builds.running_or_pending.each(&:success)
+
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'pending')
+          pipeline.builds.running_or_pending.each(&:drop)
+
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'failed', 'pending')
+          pipeline.builds.running_or_pending.each(&:success)
+
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'failed', 'success', 'pending')
+          pipeline.builds.running_or_pending.each(&:success)
+
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'failed', 'success', 'success')
+          pipeline.reload
+          expect(pipeline.status).to eq('failed')
+        end
+      end
+
+      context 'when test and test_failure jobs fail' do
+        it 'properly creates builds' do
+          expect(create_builds).to be_truthy
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('pending')
+          pipeline.builds.running_or_pending.each(&:success)
+
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'pending')
+          pipeline.builds.running_or_pending.each(&:drop)
+
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'failed', 'pending')
+          pipeline.builds.running_or_pending.each(&:drop)
+
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'failed', 'failed', 'pending')
+          pipeline.builds.running_or_pending.each(&:success)
+
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'failed', 'failed', 'success')
+          pipeline.reload
+          expect(pipeline.status).to eq('failed')
+        end
+      end
+
+      context 'when deploy job fails' do
+        it 'properly creates builds' do
+          expect(create_builds).to be_truthy
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('pending')
+          pipeline.builds.running_or_pending.each(&:success)
+
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'pending')
+          pipeline.builds.running_or_pending.each(&:success)
+
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'success', 'pending')
+          pipeline.builds.running_or_pending.each(&:drop)
+
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy', 'cleanup')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'success', 'failed', 'pending')
+          pipeline.builds.running_or_pending.each(&:success)
+
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'success', 'failed', 'success')
+          pipeline.reload
+          expect(pipeline.status).to eq('failed')
+        end
+      end
+
+      context 'when build is canceled in the second stage' do
+        it 'does not schedule builds after build has been canceled' do
+          expect(create_builds).to be_truthy
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('pending')
+          pipeline.builds.running_or_pending.each(&:success)
+
+          expect(pipeline.builds.running_or_pending).not_to be_empty
+
+          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test')
+          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'pending')
+          pipeline.builds.running_or_pending.each(&:cancel)
+
+          expect(pipeline.builds.running_or_pending).to be_empty
+          expect(pipeline.reload.status).to eq('canceled')
+        end
+      end
+    end
+  end
+
+  describe "#finished_at" do
+    let(:pipeline) { FactoryGirl.create :ci_pipeline }
+
+    it "returns finished_at of latest build" do
+      build = FactoryGirl.create :ci_build, pipeline: pipeline, finished_at: Time.now - 60
+      FactoryGirl.create :ci_build, pipeline: pipeline, finished_at: Time.now - 120
+
+      expect(pipeline.finished_at.to_i).to eq(build.finished_at.to_i)
+    end
+
+    it "returns nil if there is no finished build" do
+      FactoryGirl.create :ci_not_started_build, pipeline: pipeline
+
+      expect(pipeline.finished_at).to be_nil
+    end
+  end
+
+  describe "coverage" do
+    let(:project) { FactoryGirl.create :empty_project, build_coverage_regex: "/.*/" }
+    let(:pipeline) { FactoryGirl.create :ci_pipeline, project: project }
+
+    it "calculates average when there are two builds with coverage" do
+      FactoryGirl.create :ci_build, name: "rspec", coverage: 30, pipeline: pipeline
+      FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, pipeline: pipeline
+      expect(pipeline.coverage).to eq("35.00")
+    end
+
+    it "calculates average when there are two builds with coverage and one with nil" do
+      FactoryGirl.create :ci_build, name: "rspec", coverage: 30, pipeline: pipeline
+      FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, pipeline: pipeline
+      FactoryGirl.create :ci_build, pipeline: pipeline
+      expect(pipeline.coverage).to eq("35.00")
+    end
+
+    it "calculates average when there are two builds with coverage and one is retried" do
+      FactoryGirl.create :ci_build, name: "rspec", coverage: 30, pipeline: pipeline
+      FactoryGirl.create :ci_build, name: "rubocop", coverage: 30, pipeline: pipeline
+      FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, pipeline: pipeline
+      expect(pipeline.coverage).to eq("35.00")
+    end
+
+    it "calculates average when there is one build without coverage" do
+      FactoryGirl.create :ci_build, pipeline: pipeline
+      expect(pipeline.coverage).to be_nil
+    end
+  end
+
+  describe '#retryable?' do
+    subject { pipeline.retryable? }
+
+    context 'no failed builds' do
+      before do
+        FactoryGirl.create :ci_build, name: "rspec", pipeline: pipeline, status: 'success'
+      end
+
+      it 'be not retryable' do
+        is_expected.to be_falsey
+      end
+    end
+
+    context 'with failed builds' do
+      before do
+        FactoryGirl.create :ci_build, name: "rspec", pipeline: pipeline, status: 'running'
+        FactoryGirl.create :ci_build, name: "rubocop", pipeline: pipeline, status: 'failed'
+      end
+
+      it 'be retryable' do
+        is_expected.to be_truthy
+      end
+    end
+  end
+
+  describe '#stages' do
+    let(:pipeline2) { FactoryGirl.create :ci_pipeline, project: project }
+    subject { CommitStatus.where(pipeline: [pipeline, pipeline2]).stages }
+
+    before do
+      FactoryGirl.create :ci_build, pipeline: pipeline2, stage: 'test', stage_idx: 1
+      FactoryGirl.create :ci_build, pipeline: pipeline, stage: 'build', stage_idx: 0
+    end
+
+    it 'return all stages' do
+      is_expected.to eq(%w(build test))
+    end
+  end
+
+  describe '#update_state' do
+    it 'execute update_state after touching object' do
+      expect(pipeline).to receive(:update_state).and_return(true)
+      pipeline.touch
+    end
+
+    context 'dependent objects' do
+      let(:commit_status) { build :commit_status, pipeline: pipeline }
+
+      it 'execute update_state after saving dependent object' do
+        expect(pipeline).to receive(:update_state).and_return(true)
+        commit_status.save
+      end
+    end
+
+    context 'update state' do
+      let(:current) { Time.now.change(usec: 0) }
+      let(:build) { FactoryGirl.create :ci_build, :success, pipeline: pipeline, started_at: current - 120, finished_at: current - 60 }
+
+      before do
+        build
+      end
+
+      [:status, :started_at, :finished_at, :duration].each do |param|
+        it "update #{param}" do
+          expect(pipeline.send(param)).to eq(build.send(param))
+        end
+      end
+    end
+  end
+
+  describe '#branch?' do
+    subject { pipeline.branch? }
+
+    context 'is not a tag' do
+      before do
+        pipeline.tag = false
+      end
+
+      it 'return true when tag is set to false' do
+        is_expected.to be_truthy
+      end
+    end
+
+    context 'is not a tag' do
+      before do
+        pipeline.tag = true
+      end
+
+      it 'return false when tag is set to true' do
+        is_expected.to be_falsey
+      end
+    end
+  end
+end
diff --git a/spec/models/ci/runner_project_spec.rb b/spec/models/ci/runner_project_spec.rb
deleted file mode 100644
index 95fc160b2382acefd5a79d46f580cd5e0ab4a8a4..0000000000000000000000000000000000000000
--- a/spec/models/ci/runner_project_spec.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require 'spec_helper'
-
-describe Ci::RunnerProject, models: true do
-  pending "add some examples to (or delete) #{__FILE__}"
-end
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 7c6e39419ed455c35fef617817c834d3e5269adb..5d04d8ffcff625f49ab0547c673031ece948f55d 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -140,7 +140,7 @@ describe Ci::Runner, models: true do
 
     context 'when runner does not have tags' do
       subject { create(:ci_runner, tag_list: []) }
-      it { is_expected.to_not have_tags }
+      it { is_expected.not_to have_tags }
     end
   end
 
diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb
index c712d211b0fd69879bdb0bb1d96b03d6bc6031c8..98f60087cf5e957db2c795a7117255b01bf6f2a4 100644
--- a/spec/models/ci/variable_spec.rb
+++ b/spec/models/ci/variable_spec.rb
@@ -23,7 +23,7 @@ describe Ci::Variable, models: true do
     end
 
     it 'fails to decrypt if iv is incorrect' do
-      subject.encrypted_value_iv = nil
+      subject.encrypted_value_iv = SecureRandom.hex
       subject.instance_variable_set(:@value, nil)
       expect { subject.value }.
         to raise_error(OpenSSL::Cipher::CipherError, 'bad decrypt')
diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb
index 9307d97e2141c28c741616a79c4999a27659a80e..384a38ebc6914187ff37cb12be36b73984b844bf 100644
--- a/spec/models/commit_range_spec.rb
+++ b/spec/models/commit_range_spec.rb
@@ -24,6 +24,16 @@ describe CommitRange, models: true do
     expect { described_class.new("Foo", project) }.to raise_error(ArgumentError)
   end
 
+  describe '#initialize' do
+    it 'does not modify strings in-place' do
+      input = "#{sha_from}...#{sha_to}   "
+
+      described_class.new(input, project)
+
+      expect(input).to eq("#{sha_from}...#{sha_to}   ")
+    end
+  end
+
   describe '#to_s' do
     it 'is correct for three-dot syntax' do
       expect(range.to_s).to eq "#{full_sha_from}...#{full_sha_to}"
@@ -135,4 +145,28 @@ describe CommitRange, models: true do
       end
     end
   end
+
+  describe '#has_been_reverted?' do
+    it 'returns true if the commit has been reverted' do
+      issue = create(:issue)
+
+      create(:note_on_issue,
+             noteable: issue,
+             system: true,
+             note: commit1.revert_description,
+             project: issue.project)
+
+      expect_any_instance_of(Commit).to receive(:reverts_commit?).
+        with(commit1).
+        and_return(true)
+
+      expect(commit1.has_been_reverted?(nil, issue)).to eq(true)
+    end
+
+    it 'returns false a commit has not been reverted' do
+      issue = create(:issue)
+
+      expect(commit1.has_been_reverted?(nil, issue)).to eq(false)
+    end
+  end
 end
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index ccb100cd96fbc6295d26815d64b922b6478797bb..beca8708c9d081fe84354240303fe8f55227c7de 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -1,7 +1,7 @@
 require 'spec_helper'
 
 describe Commit, models: true do
-  let(:project) { create(:project) }
+  let(:project) { create(:project, :public) }
   let(:commit)  { project.commit }
 
   describe 'modules' do
@@ -171,4 +171,40 @@ eos
   describe '#status' do
     # TODO: kamil
   end
+
+  describe '#participants' do
+    let(:user1) { build(:user) }
+    let(:user2) { build(:user) }
+
+    let!(:note1) do
+      create(:note_on_commit,
+             commit_id: commit.id,
+             project: project,
+             note: 'foo')
+    end
+
+    let!(:note2) do
+      create(:note_on_commit,
+             commit_id: commit.id,
+             project: project,
+             note: 'bar')
+    end
+
+    before do
+      allow(commit).to receive(:author).and_return(user1)
+      allow(commit).to receive(:committer).and_return(user2)
+    end
+
+    it 'includes the commit author' do
+      expect(commit.participants).to include(commit.author)
+    end
+
+    it 'includes the committer' do
+      expect(commit.participants).to include(commit.committer)
+    end
+
+    it 'includes the authors of the commit notes' do
+      expect(commit.participants).to include(note1.author, note2.author)
+    end
+  end
 end
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index 434e58cfd0662b705f23ef4c13c58dcca202ed9e..8fb605fff8a45990fc4f5d88ec3ad87de32848ec 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -1,18 +1,18 @@
 require 'spec_helper'
 
 describe CommitStatus, models: true do
-  let(:commit) { FactoryGirl.create :ci_commit }
-  let(:commit_status) { FactoryGirl.create :commit_status, commit: commit }
+  let(:pipeline) { FactoryGirl.create :ci_pipeline }
+  let(:commit_status) { FactoryGirl.create :commit_status, pipeline: pipeline }
 
-  it { is_expected.to belong_to(:commit) }
+  it { is_expected.to belong_to(:pipeline) }
   it { is_expected.to belong_to(:user) }
   it { is_expected.to belong_to(:project) }
 
   it { is_expected.to validate_presence_of(:name) }
   it { is_expected.to validate_inclusion_of(:status).in_array(%w(pending running failed success canceled)) }
 
-  it { is_expected.to delegate_method(:sha).to(:commit) }
-  it { is_expected.to delegate_method(:short_sha).to(:commit) }
+  it { is_expected.to delegate_method(:sha).to(:pipeline) }
+  it { is_expected.to delegate_method(:short_sha).to(:pipeline) }
   
   it { is_expected.to respond_to :success? }
   it { is_expected.to respond_to :failed? }
@@ -121,11 +121,11 @@ describe CommitStatus, models: true do
     subject { CommitStatus.latest.order(:id) }
 
     before do
-      @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running'
-      @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending'
-      @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'cc', status: 'success'
-      @commit4 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'bb', status: 'success'
-      @commit5 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'success'
+      @commit1 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'aa', ref: 'bb', status: 'running'
+      @commit2 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'cc', ref: 'cc', status: 'pending'
+      @commit3 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'aa', ref: 'cc', status: 'success'
+      @commit4 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'cc', ref: 'bb', status: 'success'
+      @commit5 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'aa', ref: 'bb', status: 'success'
     end
 
     it 'return unique statuses' do
@@ -137,11 +137,11 @@ describe CommitStatus, models: true do
     subject { CommitStatus.running_or_pending.order(:id) }
 
     before do
-      @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running'
-      @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending'
-      @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: nil, status: 'success'
-      @commit4 = FactoryGirl.create :commit_status, commit: commit, name: 'dd', ref: nil, status: 'failed'
-      @commit5 = FactoryGirl.create :commit_status, commit: commit, name: 'ee', ref: nil, status: 'canceled'
+      @commit1 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'aa', ref: 'bb', status: 'running'
+      @commit2 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'cc', ref: 'cc', status: 'pending'
+      @commit3 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'aa', ref: nil, status: 'success'
+      @commit4 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'dd', ref: nil, status: 'failed'
+      @commit5 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'ee', ref: nil, status: 'canceled'
     end
 
     it 'return statuses that are running or pending' do
@@ -152,17 +152,17 @@ describe CommitStatus, models: true do
   describe '#before_sha' do
     subject { commit_status.before_sha }
 
-    context 'when no before_sha is set for ci::commit' do
-      before { commit.before_sha = nil }
+    context 'when no before_sha is set for pipeline' do
+      before { pipeline.before_sha = nil }
 
       it 'return blank sha' do
         is_expected.to eq(Gitlab::Git::BLANK_SHA)
       end
     end
 
-    context 'for before_sha set for ci::commit' do
+    context 'for before_sha set for pipeline' do
       let(:value) { '1234' }
-      before { commit.before_sha = value }
+      before { pipeline.before_sha = value }
 
       it 'return the set value' do
         is_expected.to eq(value)
@@ -172,14 +172,14 @@ describe CommitStatus, models: true do
 
   describe '#stages' do
     before do
-      FactoryGirl.create :commit_status, commit: commit, stage: 'build', stage_idx: 0, status: 'success'
-      FactoryGirl.create :commit_status, commit: commit, stage: 'build', stage_idx: 0, status: 'failed'
-      FactoryGirl.create :commit_status, commit: commit, stage: 'deploy', stage_idx: 2, status: 'running'
-      FactoryGirl.create :commit_status, commit: commit, stage: 'test', stage_idx: 1, status: 'success'
+      FactoryGirl.create :commit_status, pipeline: pipeline, stage: 'build', stage_idx: 0, status: 'success'
+      FactoryGirl.create :commit_status, pipeline: pipeline, stage: 'build', stage_idx: 0, status: 'failed'
+      FactoryGirl.create :commit_status, pipeline: pipeline, stage: 'deploy', stage_idx: 2, status: 'running'
+      FactoryGirl.create :commit_status, pipeline: pipeline, stage: 'test', stage_idx: 1, status: 'success'
     end
 
     context 'stages list' do
-      subject { CommitStatus.where(commit: commit).stages }
+      subject { CommitStatus.where(pipeline: pipeline).stages }
 
       it 'return ordered list of stages' do
         is_expected.to eq(%w(build test deploy))
@@ -187,7 +187,7 @@ describe CommitStatus, models: true do
     end
 
     context 'stages with statuses' do
-      subject { CommitStatus.where(commit: commit).stages_status }
+      subject { CommitStatus.where(pipeline: pipeline).stages_status }
 
       it 'return list of stages with statuses' do
         is_expected.to eq({
diff --git a/spec/models/concerns/access_requestable_spec.rb b/spec/models/concerns/access_requestable_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..983078769626dfe26ea90408b86cad0cd0685b9e
--- /dev/null
+++ b/spec/models/concerns/access_requestable_spec.rb
@@ -0,0 +1,40 @@
+require 'spec_helper'
+
+describe AccessRequestable do
+  describe 'Group' do
+    describe '#request_access' do
+      let(:group) { create(:group, :public) }
+      let(:user) { create(:user) }
+
+      it { expect(group.request_access(user)).to be_a(GroupMember) }
+      it { expect(group.request_access(user).user).to eq(user) }
+    end
+
+    describe '#access_requested?' do
+      let(:group) { create(:group, :public) }
+      let(:user) { create(:user) }
+
+      before { group.request_access(user) }
+
+      it { expect(group.members.request.exists?(user_id: user)).to be_truthy }
+    end
+  end
+
+  describe 'Project' do
+    describe '#request_access' do
+      let(:project) { create(:empty_project, :public) }
+      let(:user) { create(:user) }
+
+      it { expect(project.request_access(user)).to be_a(ProjectMember) }
+    end
+
+    describe '#access_requested?' do
+      let(:project) { create(:empty_project, :public) }
+      let(:user) { create(:user) }
+
+      before { project.request_access(user) }
+
+      it { expect(project.members.request.exists?(user_id: user)).to be_truthy }
+    end
+  end
+end
diff --git a/spec/models/concerns/awardable_spec.rb b/spec/models/concerns/awardable_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a371c4a18a9255b4966978c234b1a0fb2e2ac255
--- /dev/null
+++ b/spec/models/concerns/awardable_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+describe Issue, "Awardable" do
+  let!(:issue)        { create(:issue) }
+  let!(:award_emoji)  { create(:award_emoji, :downvote, awardable: issue) }
+
+  describe "Associations" do
+    it { is_expected.to have_many(:award_emoji).dependent(:destroy) }
+  end
+
+  describe "ClassMethods" do
+    let!(:issue2) { create(:issue) }
+
+    before do
+      create(:award_emoji, awardable: issue2)
+    end
+
+    it "orders on upvotes" do
+      expect(Issue.order_upvotes_desc.to_a).to eq [issue2, issue]
+    end
+
+    it "orders on downvotes" do
+      expect(Issue.order_downvotes_desc.to_a).to eq [issue, issue2]
+    end
+  end
+
+  describe "#upvotes" do
+    it "counts the number of upvotes" do
+      expect(issue.upvotes).to be 0
+    end
+  end
+
+  describe "#downvotes" do
+    it "counts the number of downvotes" do
+      expect(issue.downvotes).to be 1
+    end
+  end
+
+  describe "#toggle_award_emoji" do
+    it "adds an emoji if it isn't awarded yet" do
+      expect { issue.toggle_award_emoji("thumbsup", award_emoji.user) }.to change { AwardEmoji.count }.by(1)
+    end
+
+    it "toggles already awarded emoji" do
+      expect { issue.toggle_award_emoji("thumbsdown", award_emoji.user) }.to change { AwardEmoji.count }.by(-1)
+    end
+  end
+end
diff --git a/spec/models/concerns/elastic/issue_spec.rb b/spec/models/concerns/elastic/issue_spec.rb
index 8ad18cf46d594e001780914532e5e105a1ebaafd..3350dbe3e19b05688d4b7364d0bd6f56479a00e9 100644
--- a/spec/models/concerns/elastic/issue_spec.rb
+++ b/spec/models/concerns/elastic/issue_spec.rb
@@ -1,14 +1,14 @@
 require 'spec_helper'
 
-describe "Issue", elastic: true do
+describe Issue, elastic: true do
   before do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(true)
-    Issue.__elasticsearch__.create_index!
+    stub_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
+    described_class.__elasticsearch__.create_index!
   end
 
   after do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(false)
-    Issue.__elasticsearch__.delete_index!
+    described_class.__elasticsearch__.delete_index!
+    stub_application_setting(elasticsearch_search: false, elasticsearch_indexing: false)
   end
 
   it "searches issues" do
@@ -21,11 +21,11 @@ describe "Issue", elastic: true do
     # The issue I have no access to
     create :issue, title: 'bla-bla term'
 
-    Issue.__elasticsearch__.refresh_index!
+    described_class.__elasticsearch__.refresh_index!
 
     options = { project_ids: [project.id] }
 
-    expect(Issue.elastic_search('term', options: options).total_count).to eq(2)
+    expect(described_class.elastic_search('term', options: options).total_count).to eq(2)
   end
 
   it "returns json with all needed elements" do
diff --git a/spec/models/concerns/elastic/merge_request_spec.rb b/spec/models/concerns/elastic/merge_request_spec.rb
index 9f1ce6b4b6983301b3c78bf6e94e5b578a2dea84..d483c8b18bcb4302f658fe0491cfcb7534d91ec9 100644
--- a/spec/models/concerns/elastic/merge_request_spec.rb
+++ b/spec/models/concerns/elastic/merge_request_spec.rb
@@ -1,14 +1,14 @@
 require 'spec_helper'
 
-describe "MergeRequest", elastic: true do
+describe MergeRequest, elastic: true do
   before do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(true)
-    MergeRequest.__elasticsearch__.create_index!
+    stub_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
+    described_class.__elasticsearch__.create_index!
   end
 
   after do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(false)
-    MergeRequest.__elasticsearch__.delete_index!
+    described_class.__elasticsearch__.delete_index!
+    stub_application_setting(elasticsearch_search: false, elasticsearch_indexing: false)
   end
 
   it "searches merge requests" do
@@ -21,11 +21,11 @@ describe "MergeRequest", elastic: true do
     # The merge request you have no access to
     create :merge_request, title: 'also with term'
 
-    MergeRequest.__elasticsearch__.refresh_index!
+    described_class.__elasticsearch__.refresh_index!
 
     options = { project_ids: [project.id] }
 
-    expect(MergeRequest.elastic_search('term', options: options).total_count).to eq(2)
+    expect(described_class.elastic_search('term', options: options).total_count).to eq(2)
   end
 
   it "returns json with all needed elements" do
diff --git a/spec/models/concerns/elastic/milestone_spec.rb b/spec/models/concerns/elastic/milestone_spec.rb
index 8074ea6440773a30fbbe6a9a8bab8f46aaca3ef8..f4bab3dcf0df183e65101ba90a229ef25d0422cd 100644
--- a/spec/models/concerns/elastic/milestone_spec.rb
+++ b/spec/models/concerns/elastic/milestone_spec.rb
@@ -1,14 +1,14 @@
 require 'spec_helper'
 
-describe "Milestone", elastic: true do
+describe Milestone, elastic: true do
   before do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(true)
-    Milestone.__elasticsearch__.create_index!
+    stub_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
+    described_class.__elasticsearch__.create_index!
   end
 
   after do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(false)
-    Milestone.__elasticsearch__.delete_index!
+    described_class.__elasticsearch__.delete_index!
+    stub_application_setting(elasticsearch_search: false, elasticsearch_indexing: false)
   end
 
   it "searches milestones" do
@@ -21,11 +21,11 @@ describe "Milestone", elastic: true do
     # The milestone you have no access to
     create :milestone, title: 'bla-bla term'
 
-    Milestone.__elasticsearch__.refresh_index!
+    described_class.__elasticsearch__.refresh_index!
 
     options = { project_ids: [project.id] }
 
-    expect(Milestone.elastic_search('term', options: options).total_count).to eq(2)
+    expect(described_class.elastic_search('term', options: options).total_count).to eq(2)
   end
 
   it "returns json with all needed elements" do
diff --git a/spec/models/concerns/elastic/note_spec.rb b/spec/models/concerns/elastic/note_spec.rb
index a63e2e048c13fa929cef8c3344920e0779aaf793..2a1938c8604c4d6bc739845b318b449950dd2113 100644
--- a/spec/models/concerns/elastic/note_spec.rb
+++ b/spec/models/concerns/elastic/note_spec.rb
@@ -1,14 +1,14 @@
 require 'spec_helper'
 
-describe "Note", elastic: true do
+describe Note, elastic: true do
   before do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(true)
-    Note.__elasticsearch__.create_index!
+    stub_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
+    described_class.__elasticsearch__.create_index!
   end
 
   after do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(false)
-    Note.__elasticsearch__.delete_index!
+    described_class.__elasticsearch__.delete_index!
+    stub_application_setting(elasticsearch_search: false, elasticsearch_indexing: false)
   end
 
   it "searches notes" do
@@ -20,32 +20,97 @@ describe "Note", elastic: true do
     # The note in the project you have no access to
     create :note, note: 'bla-bla term'
 
-    Note.__elasticsearch__.refresh_index!
+    described_class.__elasticsearch__.refresh_index!
 
     options = { project_ids: [issue.project.id] }
 
-    expect(Note.elastic_search('term', options: options).total_count).to eq(1)
+    expect(described_class.elastic_search('term', options: options).total_count).to eq(1)
   end
 
   it "returns json with all needed elements" do
     note = create :note
 
-    expected_hash = note.attributes.extract!(
+    expected_hash_keys = [
       'id',
       'note',
       'project_id',
-      'created_at'
-    )
+      'created_at',
+      'issue',
+      'updated_at_sort'
+    ]
 
-    expected_hash['updated_at_sort'] = note.updated_at
+    expect(note.as_indexed_json.keys).to eq(expected_hash_keys)
+  end
+
+  it "does not create ElasticIndexerWorker job for system messages" do
+    project = create :project
+    issue = create :issue, project: project
 
-    expect(note.as_indexed_json).to eq(expected_hash)
+    # Only issue should be updated
+    expect(ElasticIndexerWorker).to receive(:perform_async).with(:update, 'Issue', anything, anything)
+    create :note, :system, project: project, noteable: issue
   end
 
-  it "does not create ElasticIndexerWorker job for award or system messages" do
-    project = create :empty_project
-    expect(ElasticIndexerWorker).to_not receive(:perform_async)
-    create :note, :system, project: project
-    create :note, :award, project: project
+  context 'notes to confidential issues' do
+    it "does not find note" do
+      issue = create :issue, :confidential
+
+      create :note, note: 'bla-bla term', project: issue.project, noteable: issue
+      create :note, project: issue.project, noteable: issue
+
+      Note.__elasticsearch__.refresh_index!
+
+      options = { project_ids: [issue.project.id] }
+
+      expect(Note.elastic_search('term', options: options).total_count).to eq(0)
+    end
+
+    it "finds note when user is authorized to see it" do
+      user = create :user
+      issue = create :issue, :confidential, author: user
+
+      create :note, note: 'bla-bla term', project: issue.project, noteable: issue
+      create :note, project: issue.project, noteable: issue
+
+      Note.__elasticsearch__.refresh_index!
+
+      options = { project_ids: [issue.project.id], current_user: user }
+
+      expect(Note.elastic_search('term', options: options).total_count).to eq(1)
+    end
+
+    it "return notes with matching content for project members" do
+      user = create :user
+      issue = create :issue, :confidential, author: user
+
+      member = create(:user)
+      issue.project.team << [member, :developer]
+
+      create :note, note: 'bla-bla term', project: issue.project, noteable: issue
+      create :note, project: issue.project, noteable: issue
+
+      Note.__elasticsearch__.refresh_index!
+
+      options = { project_ids: [issue.project.id], current_user: member }
+
+      expect(Note.elastic_search('term', options: options).total_count).to eq(1)
+    end
+
+    it "does not return notes with matching content for project members with guest role" do
+      user = create :user
+      issue = create :issue, :confidential, author: user
+
+      member = create(:user)
+      issue.project.team << [member, :guest]
+
+      create :note, note: 'bla-bla term', project: issue.project, noteable: issue
+      create :note, project: issue.project, noteable: issue
+
+      Note.__elasticsearch__.refresh_index!
+
+      options = { project_ids: [issue.project.id], current_user: member }
+
+      expect(Note.elastic_search('term', options: options).total_count).to eq(0)
+    end
   end
 end
diff --git a/spec/models/concerns/elastic/project_spec.rb b/spec/models/concerns/elastic/project_spec.rb
index 8e66c969b94584671efe06b60ea7964aea445711..80ea3cb7e67041cab5af3ad081438c21b0efa731 100644
--- a/spec/models/concerns/elastic/project_spec.rb
+++ b/spec/models/concerns/elastic/project_spec.rb
@@ -1,14 +1,14 @@
 require 'spec_helper'
 
-describe "Projects", elastic: true do
+describe Project, elastic: true do
   before do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(true)
-    Project.__elasticsearch__.create_index!
+    stub_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
+    described_class.__elasticsearch__.create_index!
   end
 
   after do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(false)
-    Project.__elasticsearch__.delete_index!
+    described_class.__elasticsearch__.delete_index!
+    stub_application_setting(elasticsearch_search: false, elasticsearch_indexing: false)
   end
 
   it "searches projects" do
@@ -18,11 +18,11 @@ describe "Projects", elastic: true do
     create :empty_project, path: 'someone_elses_project'
     project_ids = [project.id, project1.id, project2.id]
 
-    Project.__elasticsearch__.refresh_index!
+    described_class.__elasticsearch__.refresh_index!
 
-    expect(Project.elastic_search('test', options: { pids: project_ids }).total_count).to eq(1)
-    expect(Project.elastic_search('test1', options: { pids: project_ids }).total_count).to eq(1)
-    expect(Project.elastic_search('someone_elses_project', options: { pids: project_ids }).total_count).to eq(0)
+    expect(described_class.elastic_search('test', options: { pids: project_ids }).total_count).to eq(1)
+    expect(described_class.elastic_search('test1', options: { pids: project_ids }).total_count).to eq(1)
+    expect(described_class.elastic_search('someone_elses_project', options: { pids: project_ids }).total_count).to eq(0)
   end
 
   it "returns json with all needed elements" do
diff --git a/spec/models/concerns/elastic/repositories_spec.rb b/spec/models/concerns/elastic/repositories_spec.rb
index fe8a84c9e6786ef78f23dd8c9c488dcca642f243..0cdc24eca13b6c6a91211e168fcf609867185e00 100644
--- a/spec/models/concerns/elastic/repositories_spec.rb
+++ b/spec/models/concerns/elastic/repositories_spec.rb
@@ -1,14 +1,14 @@
 require 'spec_helper'
 
-describe "Repository", elastic: true do
+describe Repository, elastic: true do
   before do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(true)
-    Repository.__elasticsearch__.create_index!
+    stub_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
+    described_class.__elasticsearch__.create_index!
   end
 
   after do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(false)
-    Repository.__elasticsearch__.delete_index!
+    described_class.__elasticsearch__.delete_index!
+    stub_application_setting(elasticsearch_search: false, elasticsearch_indexing: false)
   end
 
   it "searches blobs and commits" do
@@ -16,8 +16,8 @@ describe "Repository", elastic: true do
 
     project.repository.index_blobs
     project.repository.index_commits
-    
-    Repository.__elasticsearch__.refresh_index!
+
+    described_class.__elasticsearch__.refresh_index!
 
     expect(project.repository.search('def popen')[:blobs][:total_count]).to eq(1)
     expect(project.repository.search('initial')[:commits][:total_count]).to eq(1)
diff --git a/spec/models/concerns/elastic/snippet_spec.rb b/spec/models/concerns/elastic/snippet_spec.rb
index 6ed1d33a3e68324c223697b87e560642ca126c75..aa796c0e7ba88be6454b19921f8b052613bd85ba 100644
--- a/spec/models/concerns/elastic/snippet_spec.rb
+++ b/spec/models/concerns/elastic/snippet_spec.rb
@@ -1,14 +1,14 @@
 require 'spec_helper'
 
-describe "Snippet", elastic: true do
+describe Snippet, elastic: true do
   before do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(true)
-    Snippet.__elasticsearch__.create_index!
+    stub_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
+    described_class.__elasticsearch__.create_index!
   end
 
   after do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(false)
-    Snippet.__elasticsearch__.delete_index!
+    described_class.__elasticsearch__.delete_index!
+    stub_application_setting(elasticsearch_search: false, elasticsearch_indexing: false)
   end
 
   it "searches snippets by code" do
@@ -20,11 +20,11 @@ describe "Snippet", elastic: true do
 
     snippet3 = create :personal_snippet, :public, content: 'genius code'
 
-    Snippet.__elasticsearch__.refresh_index!
+    described_class.__elasticsearch__.refresh_index!
 
     options = { author_id: user.id }
 
-    result = Snippet.elastic_search_code('genius code', options: options)
+    result = described_class.elastic_search_code('genius code', options: options)
 
     expect(result.total_count).to eq(2)
     expect(result.records.map(&:id)).to include(snippet.id, snippet3.id)
@@ -38,12 +38,12 @@ describe "Snippet", elastic: true do
     create :snippet, :public, file_name: 'index.php'
     create :snippet
 
-    Snippet.__elasticsearch__.refresh_index!
+    described_class.__elasticsearch__.refresh_index!
 
     options = { author_id: user.id }
 
-    expect(Snippet.elastic_search('home', options: options).total_count).to eq(1)
-    expect(Snippet.elastic_search('index.php', options:  options).total_count).to eq(1)
+    expect(described_class.elastic_search('home', options: options).total_count).to eq(1)
+    expect(described_class.elastic_search('index.php', options:  options).total_count).to eq(1)
   end
 
   it "returns json with all needed elements" do
diff --git a/spec/models/concerns/elastic/wiki_reposotories_spec.rb b/spec/models/concerns/elastic/wiki_reposotories_spec.rb
index 53797e36b17171f4d56971e63675ae88d76d4850..f0b226a1edfd89b76cafc12c2ece945b5f5851f3 100644
--- a/spec/models/concerns/elastic/wiki_reposotories_spec.rb
+++ b/spec/models/concerns/elastic/wiki_reposotories_spec.rb
@@ -1,14 +1,14 @@
 require 'spec_helper'
 
-describe "ProjectWiki", elastic: true do
+describe ProjectWiki, elastic: true do
   before do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(true)
-    ProjectWiki.__elasticsearch__.create_index!
+    stub_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
+    described_class.__elasticsearch__.create_index!
   end
 
   after do
-    allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(false)
-    ProjectWiki.__elasticsearch__.delete_index!
+    described_class.__elasticsearch__.delete_index!
+    stub_application_setting(elasticsearch_search: false, elasticsearch_indexing: false)
   end
 
   it "searches wiki page" do
@@ -17,8 +17,8 @@ describe "ProjectWiki", elastic: true do
     project.wiki.create_page("index_page", "Bla bla")
 
     project.wiki.index_blobs
-    
-    ProjectWiki.__elasticsearch__.refresh_index!
+
+    described_class.__elasticsearch__.refresh_index!
 
     expect(project.wiki.search('bla', type: :blob)[:blobs][:total_count]).to eq(1)
   end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index cca2f7db74c17b4e4870a60a8b121c14d8831237..9610937428c4b3e0a07d393be8daa4ff072fa5a8 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -10,6 +10,20 @@ describe Issue, "Issuable" do
     it { is_expected.to belong_to(:assignee) }
     it { is_expected.to have_many(:notes).dependent(:destroy) }
     it { is_expected.to have_many(:todos).dependent(:destroy) }
+
+    context 'Notes' do
+      let!(:note) { create(:note, noteable: issue, project: issue.project) }
+      let(:scoped_issue) { Issue.includes(notes: :author).find(issue.id) }
+
+      it 'indicates if the notes have their authors loaded' do
+        expect(issue.notes).not_to be_authors_loaded
+        expect(scoped_issue.notes).to be_authors_loaded
+      end
+    end
+  end
+
+  describe 'Included modules' do
+    it { is_expected.to include_module(Awardable) }
   end
 
   describe "Validation" do
@@ -133,6 +147,30 @@ describe Issue, "Issuable" do
         expect(issues).to match_array([issue2, issue3, issue4, issue])
       end
     end
+
+    context "by milestone due date" do
+      # Correct order is:
+      # Issues/MRs with milestones ordered by date
+      # Issues/MRs with milestones without dates
+      # Issues/MRs without milestones
+
+      let!(:issue) { create(:issue, project: project) }
+      let!(:early_milestone) { create(:milestone, project: project, due_date: 10.days.from_now) }
+      let!(:late_milestone) { create(:milestone, project: project, due_date: 30.days.from_now) }
+      let!(:issue1) { create(:issue, project: project, milestone: early_milestone) }
+      let!(:issue2) { create(:issue, project: project, milestone: late_milestone) }
+      let!(:issue3) { create(:issue, project: project) }
+
+      it "sorts desc" do
+        issues = project.issues.sort('milestone_due_desc')
+        expect(issues).to match_array([issue2, issue1, issue, issue3])
+      end
+
+      it "sorts asc" do
+        issues = project.issues.sort('milestone_due_asc')
+        expect(issues).to match_array([issue1, issue2, issue, issue3])
+      end
+    end
   end
 
   describe '#subscribed?' do
@@ -181,12 +219,11 @@ describe Issue, "Issuable" do
     let(:data) { issue.to_hook_data(user) }
     let(:project) { issue.project }
 
-
     it "returns correct hook data" do
       expect(data[:object_kind]).to eq("issue")
       expect(data[:user]).to eq(user.hook_attrs)
       expect(data[:object_attributes]).to eq(issue.hook_attrs)
-      expect(data).to_not have_key(:assignee)
+      expect(data).not_to have_key(:assignee)
     end
 
     context "issue is assigned" do
@@ -220,12 +257,42 @@ describe Issue, "Issuable" do
     end
   end
 
+  describe '#labels_array' do
+    let(:project) { create(:project) }
+    let(:bug) { create(:label, project: project, title: 'bug') }
+    let(:issue) { create(:issue, project: project) }
+
+    before(:each) do
+      issue.labels << bug
+    end
+
+    it 'loads the association and returns it as an array' do
+      expect(issue.reload.labels_array).to eq([bug])
+    end
+  end
+
+  describe '#user_notes_count' do
+    let(:project) { create(:project) }
+    let(:issue1) { create(:issue, project: project) }
+    let(:issue2) { create(:issue, project: project) }
+
+    before do
+      create_list(:note, 3, noteable: issue1, project: project)
+      create_list(:note, 6, noteable: issue2, project: project)
+    end
+
+    it 'counts the user notes' do
+      expect(issue1.user_notes_count).to be(3)
+      expect(issue2.user_notes_count).to be(6)
+    end
+  end
+
   describe "votes" do
+    let(:project) { issue.project }
+
     before do
-      author = create :user
-      project = create :empty_project
-      issue.notes.awards.create!(note: "thumbsup", author: author, project: project)
-      issue.notes.awards.create!(note: "thumbsdown", author: author, project: project)
+      create(:award_emoji, :upvote, awardable: issue)
+      create(:award_emoji, :downvote, awardable: issue)
     end
 
     it "returns correct values" do
diff --git a/spec/models/concerns/milestoneish_spec.rb b/spec/models/concerns/milestoneish_spec.rb
index 47c3be673c56815d3828be81793f5c2fe9f87303..7e9ab8940cfa112792d1ec13b320c54f8578dce7 100644
--- a/spec/models/concerns/milestoneish_spec.rb
+++ b/spec/models/concerns/milestoneish_spec.rb
@@ -5,6 +5,7 @@ describe Milestone, 'Milestoneish' do
   let(:assignee) { create(:user) }
   let(:non_member) { create(:user) }
   let(:member) { create(:user) }
+  let(:guest) { create(:user) }
   let(:admin) { create(:admin) }
   let(:project) { create(:project, :public) }
   let(:milestone) { create(:milestone, project: project) }
@@ -21,6 +22,7 @@ describe Milestone, 'Milestoneish' do
 
   before do
     project.team << [member, :developer]
+    project.team << [guest, :guest]
   end
 
   describe '#closed_items_count' do
@@ -28,6 +30,10 @@ describe Milestone, 'Milestoneish' do
       expect(milestone.closed_items_count(non_member)).to eq 2
     end
 
+    it 'should not count confidential issues for project members with guest role' do
+      expect(milestone.closed_items_count(guest)).to eq 2
+    end
+
     it 'should count confidential issues for author' do
       expect(milestone.closed_items_count(author)).to eq 4
     end
@@ -50,6 +56,10 @@ describe Milestone, 'Milestoneish' do
       expect(milestone.total_items_count(non_member)).to eq 4
     end
 
+    it 'should not count confidential issues for project members with guest role' do
+      expect(milestone.total_items_count(guest)).to eq 4
+    end
+
     it 'should count confidential issues for author' do
       expect(milestone.total_items_count(author)).to eq 7
     end
@@ -85,6 +95,10 @@ describe Milestone, 'Milestoneish' do
       expect(milestone.percent_complete(non_member)).to eq 50
     end
 
+    it 'should not count confidential issues for project members with guest role' do
+      expect(milestone.percent_complete(guest)).to eq 50
+    end
+
     it 'should count confidential issues for author' do
       expect(milestone.percent_complete(author)).to eq 57
     end
diff --git a/spec/models/concerns/participable_spec.rb b/spec/models/concerns/participable_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7e4ea0f2d661d945b4d718972a64e54500520883
--- /dev/null
+++ b/spec/models/concerns/participable_spec.rb
@@ -0,0 +1,83 @@
+require 'spec_helper'
+
+describe Participable, models: true do
+  let(:model) do
+    Class.new do
+      include Participable
+    end
+  end
+
+  describe '.participant' do
+    it 'adds the participant attributes to the existing list' do
+      model.participant(:foo)
+      model.participant(:bar)
+
+      expect(model.participant_attrs).to eq([:foo, :bar])
+    end
+  end
+
+  describe '#participants' do
+    it 'returns the list of participants' do
+      model.participant(:foo)
+      model.participant(:bar)
+
+      user1 = build(:user)
+      user2 = build(:user)
+      user3 = build(:user)
+      project = build(:project, :public)
+      instance = model.new
+
+      expect(instance).to receive(:foo).and_return(user2)
+      expect(instance).to receive(:bar).and_return(user3)
+      expect(instance).to receive(:project).twice.and_return(project)
+
+      participants = instance.participants(user1)
+
+      expect(participants).to include(user2)
+      expect(participants).to include(user3)
+    end
+
+    it 'supports attributes returning another Participable' do
+      other_model = Class.new { include Participable }
+
+      other_model.participant(:bar)
+      model.participant(:foo)
+
+      instance = model.new
+      other = other_model.new
+      user1 = build(:user)
+      user2 = build(:user)
+      project = build(:project, :public)
+
+      expect(instance).to receive(:foo).and_return(other)
+      expect(other).to receive(:bar).and_return(user2)
+      expect(instance).to receive(:project).twice.and_return(project)
+
+      expect(instance.participants(user1)).to eq([user2])
+    end
+
+    context 'when using a Proc as an attribute' do
+      it 'calls the supplied Proc' do
+        user1 = build(:user)
+        project = build(:project, :public)
+
+        user_arg = nil
+        ext_arg = nil
+
+        model.participant -> (user, ext) do
+          user_arg = user
+          ext_arg = ext
+        end
+
+        instance = model.new
+
+        expect(instance).to receive(:project).twice.and_return(project)
+
+        instance.participants(user1)
+
+        expect(user_arg).to eq(user1)
+        expect(ext_arg).to be_an_instance_of(Gitlab::ReferenceExtractor)
+      end
+    end
+  end
+end
diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb
index 30c0a04b84045b315094c1fb9a9b380de70f4a53..9e8ebc56a316f14bedc409a4c8fdf098b9ea591d 100644
--- a/spec/models/concerns/token_authenticatable_spec.rb
+++ b/spec/models/concerns/token_authenticatable_spec.rb
@@ -28,14 +28,14 @@ describe ApplicationSetting, 'TokenAuthenticatable' do
     context 'token is not generated yet' do
       describe 'token field accessor' do
         subject { described_class.new.send(token_field) }
-        it { is_expected.to_not be_blank }
+        it { is_expected.not_to be_blank }
       end
 
       describe 'ensured token' do
         subject { described_class.new.send("ensure_#{token_field}") }
 
         it { is_expected.to be_a String }
-        it { is_expected.to_not be_blank }
+        it { is_expected.not_to be_blank }
       end
 
       describe 'ensured! token' do
@@ -49,7 +49,7 @@ describe ApplicationSetting, 'TokenAuthenticatable' do
 
     context 'token is generated' do
       before { subject.send("reset_#{token_field}!") }
-      it 'persists a new token 'do
+      it 'persists a new token' do
         expect(subject.send(:read_attribute, token_field)).to be_a String
       end
     end
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b273018707f491a8d2cf3c53a201ac78f998e164
--- /dev/null
+++ b/spec/models/deployment_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+
+describe Deployment, models: true do
+  subject { build(:deployment) }
+
+  it { is_expected.to belong_to(:project) }
+  it { is_expected.to belong_to(:environment) }
+  it { is_expected.to belong_to(:user) }
+  it { is_expected.to belong_to(:deployable) }
+
+  it { is_expected.to delegate_method(:name).to(:environment).with_prefix }
+  it { is_expected.to delegate_method(:commit).to(:project) }
+  it { is_expected.to delegate_method(:commit_title).to(:commit).as(:try) }
+
+  it { is_expected.to validate_presence_of(:ref) }
+  it { is_expected.to validate_presence_of(:sha) }
+end
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7629af6a570a6f101df9576e02cf0d6fbbc44ee8
--- /dev/null
+++ b/spec/models/environment_spec.rb
@@ -0,0 +1,14 @@
+require 'spec_helper'
+
+describe Environment, models: true do
+  let(:environment) { create(:environment) }
+
+  it { is_expected.to belong_to(:project) }
+  it { is_expected.to have_many(:deployments) }
+
+  it { is_expected.to delegate_method(:last_deployment).to(:deployments).as(:last) }
+
+  it { is_expected.to validate_presence_of(:name) }
+  it { is_expected.to validate_uniqueness_of(:name).scoped_to(:project_id) }
+  it { is_expected.to validate_length_of(:name).is_within(0..255) }
+end
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index b0e76fec6935cbea12ce9d26756832e7c325cf28..166a1dc4ddb52a2f75c6a19cc2dc94b579f44503 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -50,6 +50,7 @@ describe Event, models: true do
     let(:project) { create(:empty_project, :public) }
     let(:non_member) { create(:user) }
     let(:member)  { create(:user) }
+    let(:guest)  { create(:user) }
     let(:author) { create(:author) }
     let(:assignee) { create(:user) }
     let(:admin) { create(:admin) }
@@ -61,6 +62,7 @@ describe Event, models: true do
 
     before do
       project.team << [member, :developer]
+      project.team << [guest, :guest]
     end
 
     context 'issue event' do
@@ -71,6 +73,7 @@ describe Event, models: true do
         it { expect(event.visible_to_user?(author)).to eq true }
         it { expect(event.visible_to_user?(assignee)).to eq true }
         it { expect(event.visible_to_user?(member)).to eq true }
+        it { expect(event.visible_to_user?(guest)).to eq true }
         it { expect(event.visible_to_user?(admin)).to eq true }
       end
 
@@ -81,6 +84,7 @@ describe Event, models: true do
         it { expect(event.visible_to_user?(author)).to eq true }
         it { expect(event.visible_to_user?(assignee)).to eq true }
         it { expect(event.visible_to_user?(member)).to eq true }
+        it { expect(event.visible_to_user?(guest)).to eq false }
         it { expect(event.visible_to_user?(admin)).to eq true }
       end
     end
@@ -93,6 +97,7 @@ describe Event, models: true do
         it { expect(event.visible_to_user?(author)).to eq true }
         it { expect(event.visible_to_user?(assignee)).to eq true }
         it { expect(event.visible_to_user?(member)).to eq true }
+        it { expect(event.visible_to_user?(guest)).to eq true }
         it { expect(event.visible_to_user?(admin)).to eq true }
       end
 
@@ -103,6 +108,7 @@ describe Event, models: true do
         it { expect(event.visible_to_user?(author)).to eq true }
         it { expect(event.visible_to_user?(assignee)).to eq true }
         it { expect(event.visible_to_user?(member)).to eq true }
+        it { expect(event.visible_to_user?(guest)).to eq false }
         it { expect(event.visible_to_user?(admin)).to eq true }
       end
     end
diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb
index 0caf5869c24fb6eb3c05d5f99679f80bce2069b7..c4e781dd1dcaba14d3a230c382a3ade2aab264ed 100644
--- a/spec/models/generic_commit_status_spec.rb
+++ b/spec/models/generic_commit_status_spec.rb
@@ -1,8 +1,8 @@
 require 'spec_helper'
 
 describe GenericCommitStatus, models: true do
-  let(:commit) { FactoryGirl.create :ci_commit }
-  let(:generic_commit_status) { FactoryGirl.create :generic_commit_status, commit: commit }
+  let(:pipeline) { FactoryGirl.create :ci_pipeline }
+  let(:generic_commit_status) { FactoryGirl.create :generic_commit_status, pipeline: pipeline }
 
   describe :context do
     subject { generic_commit_status.context }
@@ -27,13 +27,13 @@ describe GenericCommitStatus, models: true do
     describe :context do
       subject { generic_commit_status.context }
 
-      it { is_expected.to_not be_nil }
+      it { is_expected.not_to be_nil }
     end
 
     describe :stage do
       subject { generic_commit_status.stage }
 
-      it { is_expected.to_not be_nil }
+      it { is_expected.not_to be_nil }
     end
   end
 end
diff --git a/spec/models/geo_node_spec.rb b/spec/models/geo_node_spec.rb
index 1dfbde1b9dcaa23b66e159c5edf4490d1fb20080..37bc21877c9eedeeacea0cabef40983c87812f95 100644
--- a/spec/models/geo_node_spec.rb
+++ b/spec/models/geo_node_spec.rb
@@ -8,6 +8,7 @@ describe GeoNode, type: :model do
   subject(:node) { FactoryGirl.create(:geo_node) }
 
   let(:dummy_url) { 'https://localhost:3000/gitlab' }
+  let(:url_helpers) { Gitlab::Application.routes.url_helpers }
 
   context 'associations' do
     it { is_expected.to belong_to(:geo_node_key).dependent(:destroy) }
@@ -186,6 +187,25 @@ describe GeoNode, type: :model do
     it 'returns oauth callback url based on node uri' do
       expect(new_node.oauth_callback_url).to eq(oauth_callback_url)
     end
+
+    it 'returns url that matches rails url_helpers generated one' do
+      route = url_helpers.oauth_geo_callback_url(protocol: 'https:', host: 'localhost', port: 3000, script_name: '/gitlab')
+      expect(new_node.oauth_callback_url).to eq(route)
+    end
+  end
+
+  describe '#oauth_logout_url' do
+    let(:fake_state) { URI.encode('fakestate') }
+    let(:oauth_logout_url) { "https://localhost:3000/gitlab/oauth/geo/logout?state=#{fake_state}" }
+
+    it 'returns oauth logout url based on node uri' do
+      expect(new_node.oauth_logout_url(fake_state)).to eq(oauth_logout_url)
+    end
+
+    it 'returns url that matches rails url_helpers generated one' do
+      route = url_helpers.oauth_geo_logout_url(protocol: 'https:', host: 'localhost', port: 3000, script_name: '/gitlab', state: fake_state)
+      expect(new_node.oauth_logout_url(fake_state)).to eq(route)
+    end
   end
 
   describe '#missing_oauth_application?' do
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 6fa16be7f045bb1e6be3e77800955dc820e6f168..ccdcb29f773016e5fdafe769c7bfa8b50347d87d 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -5,7 +5,11 @@ describe Group, models: true do
 
   describe 'associations' do
     it { is_expected.to have_many :projects }
-    it { is_expected.to have_many :group_members }
+    it { is_expected.to have_many(:group_members).dependent(:destroy) }
+    it { is_expected.to have_many(:users).through(:group_members) }
+    it { is_expected.to have_many(:project_group_links).dependent(:destroy) }
+    it { is_expected.to have_many(:shared_projects).through(:project_group_links) }
+    it { is_expected.to have_many(:notification_settings).dependent(:destroy) }
   end
 
   describe 'modules' do
@@ -131,4 +135,46 @@ describe Group, models: true do
       expect(described_class.search(group.path.upcase)).to eq([group])
     end
   end
+
+  describe '#has_owner?' do
+    before { @members = setup_group_members(group) }
+
+    it { expect(group.has_owner?(@members[:owner])).to be_truthy }
+    it { expect(group.has_owner?(@members[:master])).to be_falsey }
+    it { expect(group.has_owner?(@members[:developer])).to be_falsey }
+    it { expect(group.has_owner?(@members[:reporter])).to be_falsey }
+    it { expect(group.has_owner?(@members[:guest])).to be_falsey }
+    it { expect(group.has_owner?(@members[:requester])).to be_falsey }
+  end
+
+  describe '#has_master?' do
+    before { @members = setup_group_members(group) }
+
+    it { expect(group.has_master?(@members[:owner])).to be_falsey }
+    it { expect(group.has_master?(@members[:master])).to be_truthy }
+    it { expect(group.has_master?(@members[:developer])).to be_falsey }
+    it { expect(group.has_master?(@members[:reporter])).to be_falsey }
+    it { expect(group.has_master?(@members[:guest])).to be_falsey }
+    it { expect(group.has_master?(@members[:requester])).to be_falsey }
+  end
+
+  def setup_group_members(group)
+    members = {
+      owner: create(:user),
+      master: create(:user),
+      developer: create(:user),
+      reporter: create(:user),
+      guest: create(:user),
+      requester: create(:user)
+    }
+
+    group.add_user(members[:owner], GroupMember::OWNER)
+    group.add_user(members[:master], GroupMember::MASTER)
+    group.add_user(members[:developer], GroupMember::DEVELOPER)
+    group.add_user(members[:reporter], GroupMember::REPORTER)
+    group.add_user(members[:guest], GroupMember::GUEST)
+    group.request_access(members[:requester])
+
+    members
+  end
 end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 8ab00c70f9d341a35f7e8ec248366e0cecc55d64..b87d68283e6cb279c14ce737def53791ea39acf9 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -192,7 +192,7 @@ describe Issue, models: true do
                                                source_project: subject.project,
                                                source_branch: "#{subject.iid}-branch" })
       merge_request.create_cross_references!(user)
-      expect(subject.referenced_merge_requests).to_not be_empty
+      expect(subject.referenced_merge_requests).not_to be_empty
       expect(subject.related_branches(user)).to eq([subject.to_branch_name])
     end
 
@@ -231,4 +231,59 @@ describe Issue, models: true do
       expect(issue.to_branch_name).to match /confidential-issue\z/
     end
   end
+
+  describe '#participants' do
+    context 'using a public project' do
+      let(:project) { create(:project, :public) }
+      let(:issue) { create(:issue, project: project) }
+
+      let!(:note1) do
+        create(:note_on_issue, noteable: issue, project: project, note: 'a')
+      end
+
+      let!(:note2) do
+        create(:note_on_issue, noteable: issue, project: project, note: 'b')
+      end
+
+      it 'includes the issue author' do
+        expect(issue.participants).to include(issue.author)
+      end
+
+      it 'includes the authors of the notes' do
+        expect(issue.participants).to include(note1.author, note2.author)
+      end
+    end
+
+    context 'using a private project' do
+      it 'does not include mentioned users that do not have access to the project' do
+        project = create(:project)
+        user = create(:user)
+        issue = create(:issue, project: project)
+
+        create(:note_on_issue,
+               noteable: issue,
+               project: project,
+               note: user.to_reference)
+
+        expect(issue.participants).not_to include(user)
+      end
+    end
+  end
+
+  describe 'cached counts' do
+    it 'updates when assignees change' do
+      user1 = create(:user)
+      user2 = create(:user)
+      issue = create(:issue, assignee: user1)
+
+      expect(user1.assigned_open_issues_count).to eq(1)
+      expect(user2.assigned_open_issues_count).to eq(0)
+
+      issue.assignee = user2
+      issue.save
+
+      expect(user1.assigned_open_issues_count).to eq(0)
+      expect(user2.assigned_open_issues_count).to eq(1)
+    end
+  end
 end
diff --git a/spec/models/legacy_diff_note_spec.rb b/spec/models/legacy_diff_note_spec.rb
index 7c29bef54e48b8993c02d7f32e04eaccfeceddc2..b2d068538864d05f6808c51724fc243950b96ba2 100644
--- a/spec/models/legacy_diff_note_spec.rb
+++ b/spec/models/legacy_diff_note_spec.rb
@@ -63,7 +63,9 @@ describe LegacyDiffNote, models: true do
         code = Gitlab::Diff::LineCode.generate(diff.new_path, line.new_pos, line.old_pos)
 
         # We're persisting in order to trigger the set_diff callback
-        note = create(:note_on_merge_request_diff, noteable: merge, line_code: code)
+        note = create(:note_on_merge_request_diff, noteable: merge,
+                                                   line_code: code,
+                                                   project: merge.source_project)
 
         # Make sure we don't get a false positive from a guard clause
         expect(note).to receive(:find_noteable_diff).and_call_original
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index 6e51730eecd207ff08e016b9c28913cd9d8fdb89..3ed3202ac6c5e52821c42a1090b72752e8ceff1d 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -55,11 +55,97 @@ describe Member, models: true do
     end
   end
 
+  describe 'Scopes & finders' do
+    before do
+      project = create(:project)
+      group = create(:group)
+      @owner_user = create(:user).tap { |u| group.add_owner(u) }
+      @owner = group.members.find_by(user_id: @owner_user.id)
+
+      @master_user = create(:user).tap { |u| project.team << [u, :master] }
+      @master = project.members.find_by(user_id: @master_user.id)
+
+      ProjectMember.add_user(project.members, 'toto1@example.com', Gitlab::Access::DEVELOPER, @master_user)
+      @invited_member = project.members.invite.find_by_invite_email('toto1@example.com')
+
+      accepted_invite_user = build(:user)
+      ProjectMember.add_user(project.members, 'toto2@example.com', Gitlab::Access::DEVELOPER, @master_user)
+      @accepted_invite_member = project.members.invite.find_by_invite_email('toto2@example.com').tap { |u| u.accept_invite!(accepted_invite_user) }
+
+      requested_user = create(:user).tap { |u| project.request_access(u) }
+      @requested_member = project.members.request.find_by(user_id: requested_user.id)
+
+      accepted_request_user = create(:user).tap { |u| project.request_access(u) }
+      @accepted_request_member = project.members.request.find_by(user_id: accepted_request_user.id).tap { |m| m.accept_request }
+    end
+
+    describe '.invite' do
+      it { expect(described_class.invite).not_to include @master }
+      it { expect(described_class.invite).to include @invited_member }
+      it { expect(described_class.invite).not_to include @accepted_invite_member }
+      it { expect(described_class.invite).not_to include @requested_member }
+      it { expect(described_class.invite).not_to include @accepted_request_member }
+    end
+
+    describe '.non_invite' do
+      it { expect(described_class.non_invite).to include @master }
+      it { expect(described_class.non_invite).not_to include @invited_member }
+      it { expect(described_class.non_invite).to include @accepted_invite_member }
+      it { expect(described_class.non_invite).to include @requested_member }
+      it { expect(described_class.non_invite).to include @accepted_request_member }
+    end
+
+    describe '.request' do
+      it { expect(described_class.request).not_to include @master }
+      it { expect(described_class.request).not_to include @invited_member }
+      it { expect(described_class.request).not_to include @accepted_invite_member }
+      it { expect(described_class.request).to include @requested_member }
+      it { expect(described_class.request).not_to include @accepted_request_member }
+    end
+
+    describe '.non_request' do
+      it { expect(described_class.non_request).to include @master }
+      it { expect(described_class.non_request).to include @invited_member }
+      it { expect(described_class.non_request).to include @accepted_invite_member }
+      it { expect(described_class.non_request).not_to include @requested_member }
+      it { expect(described_class.non_request).to include @accepted_request_member }
+    end
+
+    describe '.non_pending' do
+      it { expect(described_class.non_pending).to include @master }
+      it { expect(described_class.non_pending).not_to include @invited_member }
+      it { expect(described_class.non_pending).to include @accepted_invite_member }
+      it { expect(described_class.non_pending).not_to include @requested_member }
+      it { expect(described_class.non_pending).to include @accepted_request_member }
+    end
+
+    describe '.owners_and_masters' do
+      it { expect(described_class.owners_and_masters).to include @owner }
+      it { expect(described_class.owners_and_masters).to include @master }
+      it { expect(described_class.owners_and_masters).not_to include @invited_member }
+      it { expect(described_class.owners_and_masters).not_to include @accepted_invite_member }
+      it { expect(described_class.owners_and_masters).not_to include @requested_member }
+      it { expect(described_class.owners_and_masters).not_to include @accepted_request_member }
+    end
+  end
+
   describe "Delegate methods" do
     it { is_expected.to respond_to(:user_name) }
     it { is_expected.to respond_to(:user_email) }
   end
 
+  describe 'Callbacks' do
+    describe 'after_destroy :post_decline_request, if: :request?' do
+      let(:member) { create(:project_member, requested_at: Time.now.utc) }
+
+      it 'calls #post_decline_request' do
+        expect(member).to receive(:post_decline_request)
+
+        member.destroy
+      end
+    end
+  end
+
   describe ".add_user" do
     let!(:user)    { create(:user) }
     let(:project) { create(:project) }
@@ -97,6 +183,44 @@ describe Member, models: true do
     end
   end
 
+  describe '#accept_request' do
+    let(:member) { create(:project_member, requested_at: Time.now.utc) }
+
+    it { expect(member.accept_request).to be_truthy }
+
+    it 'clears requested_at' do
+      member.accept_request
+
+      expect(member.requested_at).to be_nil
+    end
+
+    it 'calls #after_accept_request' do
+      expect(member).to receive(:after_accept_request)
+
+      member.accept_request
+    end
+  end
+
+  describe '#invite?' do
+    subject { create(:project_member, invite_email: "user@example.com", user: nil) }
+
+    it { is_expected.to be_invite }
+  end
+
+  describe '#request?' do
+    subject { create(:project_member, requested_at: Time.now.utc) }
+
+    it { is_expected.to be_request }
+  end
+
+  describe '#pending?' do
+    let(:invited_member) { create(:project_member, invite_email: "user@example.com", user: nil) }
+    let(:requester) { create(:project_member, requested_at: Time.now.utc) }
+
+    it { expect(invited_member).to be_invite }
+    it { expect(requester).to be_pending }
+  end
+
   describe "#accept_invite!" do
     let!(:member) { create(:project_member, invite_email: "user@example.com", user: nil) }
     let(:user) { create(:user) }
diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb
index 5424c9b9cba9fb4dc96b6aa40dee2597b45ce4b7..eeb74a462acb2b077c0f2d7dc438df81736a143e 100644
--- a/spec/models/members/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
@@ -20,7 +20,7 @@
 require 'spec_helper'
 
 describe GroupMember, models: true do
-  context 'notification' do
+  describe 'notifications' do
     describe "#after_create" do
       it "should send email to user" do
         membership = build(:group_member)
@@ -50,5 +50,31 @@ describe GroupMember, models: true do
         @group_member.update_attribute(:access_level, GroupMember::OWNER)
       end
     end
+
+    describe '#after_accept_request' do
+      it 'calls NotificationService.accept_group_access_request' do
+        member = create(:group_member, user: build_stubbed(:user), requested_at: Time.now)
+
+        expect_any_instance_of(NotificationService).to receive(:new_group_member)
+
+        member.__send__(:after_accept_request)
+      end
+    end
+
+    describe '#post_decline_request' do
+      it 'calls NotificationService.decline_group_access_request' do
+        member = create(:group_member, user: build_stubbed(:user), requested_at: Time.now)
+
+        expect_any_instance_of(NotificationService).to receive(:decline_group_access_request)
+
+        member.__send__(:post_decline_request)
+      end
+    end
+
+    describe '#real_source_type' do
+      subject { create(:group_member).real_source_type }
+
+      it { is_expected.to eq 'Group' }
+    end
   end
 end
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index 9f26d9eb5ce592805dcae168b7cabd57847ec31b..1e466f9c62045ef243b5b175bb61dc6fbd4ec3eb 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -20,6 +20,54 @@
 require 'spec_helper'
 
 describe ProjectMember, models: true do
+  describe 'associations' do
+    it { is_expected.to belong_to(:project).class_name('Project').with_foreign_key(:source_id) }
+  end
+
+  describe 'validations' do
+    it { is_expected.to allow_value('Project').for(:source_type) }
+    it { is_expected.not_to allow_value('project').for(:source_type) }
+  end
+
+  describe 'modules' do
+    it { is_expected.to include_module(Gitlab::ShellAdapter) }
+  end
+
+  describe '#real_source_type' do
+    subject { create(:project_member).real_source_type }
+
+    it { is_expected.to eq 'Project' }
+  end
+
+  describe "#destroy" do
+    let(:owner)   { create(:project_member, access_level: ProjectMember::OWNER) }
+    let(:project) { owner.project }
+    let(:master)  { create(:project_member, project: project) }
+
+    let(:owner_todos)  { (0...2).map { create(:todo, user: owner.user, project: project) } }
+    let(:master_todos) { (0...3).map { create(:todo, user: master.user, project: project) } }
+
+    before do
+      owner_todos
+      master_todos
+    end
+
+    it "destroy itself and delete associated todos" do
+      expect(owner.user.todos.size).to eq(2)
+      expect(master.user.todos.size).to eq(3)
+      expect(Todo.count).to eq(5)
+
+      master_todo_ids = master_todos.map(&:id)
+      master.destroy
+
+      expect(owner.user.todos.size).to eq(2)
+      expect(Todo.count).to eq(2)
+      master_todo_ids.each do |id|
+        expect(Todo.exists?(id)).to eq(false)
+      end
+    end
+  end
+
   describe :import_team do
     before do
       @abilities = Six.new
@@ -93,4 +141,26 @@ describe ProjectMember, models: true do
     it { expect(@project_1.users).to be_empty }
     it { expect(@project_2.users).to be_empty }
   end
+
+  describe 'notifications' do
+    describe '#after_accept_request' do
+      it 'calls NotificationService.new_project_member' do
+        member = create(:project_member, user: build_stubbed(:user), requested_at: Time.now)
+
+        expect_any_instance_of(NotificationService).to receive(:new_project_member)
+
+        member.__send__(:after_accept_request)
+      end
+    end
+
+    describe '#post_decline_request' do
+      it 'calls NotificationService.decline_project_access_request' do
+        member = create(:project_member, user: build_stubbed(:user), requested_at: Time.now)
+
+        expect_any_instance_of(NotificationService).to receive(:decline_project_access_request)
+
+        member.__send__(:post_decline_request)
+      end
+    end
+  end
 end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 50818e476d9a86fae3cc32dae1e08b0d663f0601..826d19f4cf84d5a7aaead425325ae7fc59b5d5cf 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -119,7 +119,8 @@ describe MergeRequest, models: true do
 
     before do
       allow(merge_request).to receive(:commits) { [merge_request.source_project.repository.commit] }
-      create(:note, commit_id: merge_request.commits.first.id, noteable_type: 'Commit', project: merge_request.project)
+      create(:note_on_commit, commit_id: merge_request.commits.first.id,
+                              project: merge_request.project)
       create(:note, noteable: merge_request, project: merge_request.project)
     end
 
@@ -129,7 +130,9 @@ describe MergeRequest, models: true do
     end
 
     it "should include notes for commits from target project as well" do
-      create(:note, commit_id: merge_request.commits.first.id, noteable_type: 'Commit', project: merge_request.target_project)
+      create(:note_on_commit, commit_id: merge_request.commits.first.id,
+                              project: merge_request.target_project)
+
       expect(merge_request.commits).not_to be_empty
       expect(merge_request.mr_and_commit_notes.count).to eq(3)
     end
@@ -220,7 +223,7 @@ describe MergeRequest, models: true do
     end
   end
 
-  describe "approvers_left" do
+  describe "#approvers_left" do
     let(:merge_request) {create :merge_request}
 
     it "returns correct value" do
@@ -234,7 +237,7 @@ describe MergeRequest, models: true do
     end
   end
 
-  describe "approvals_required" do
+  describe "#approvals_required" do
     let(:merge_request) {create :merge_request}
 
     it "takes approvals_before_merge" do
@@ -244,6 +247,58 @@ describe MergeRequest, models: true do
     end
   end
 
+  describe "#can_approve?" do
+    let(:author) { create(:user) }
+    let(:user) { create(:user) }
+    let(:merge_request) { create(:merge_request, author: author) }
+
+    context "when the user is the MR author" do
+      it "returns false" do
+        expect(merge_request.can_approve?(author)).to eq(false)
+      end
+    end
+
+    context "when the user is not the MR author" do
+      context "when the user is in the approvers list" do
+        before { merge_request.approvers.create(user: user) }
+
+        context "when the user has not already approved the MR" do
+          it "returns true" do
+            expect(merge_request.can_approve?(user)).to eq(true)
+          end
+        end
+
+        context "when the user has already approved the MR" do
+          before { merge_request.approvals.create(user: user) }
+
+          it "returns false" do
+            expect(merge_request.can_approve?(user)).to eq(false)
+          end
+        end
+      end
+
+      context "when the user is not in the approvers list" do
+        context "when anyone is allowed to approve the MR" do
+          before { merge_request.target_project.update_attributes(approvals_before_merge: 1) }
+
+          context "when the user has not already approved the MR" do
+            it "returns true" do
+              expect(merge_request.can_approve?(user)).to eq(true)
+            end
+          end
+
+          context "when the user has already approved the MR" do
+            before { merge_request.approvals.create(user: user) }
+
+            it "returns false" do
+              expect(merge_request.can_approve?(user)).to eq(false)
+            end
+          end
+        end
+      end
+    end
+  end
+
   describe '#can_remove_source_branch?' do
     let(:user) { create(:user) }
     let(:user2) { create(:user) }
@@ -417,19 +472,19 @@ describe MergeRequest, models: true do
     subject { create :merge_request, :simple }
   end
 
-  describe '#ci_commit' do
+  describe '#pipeline' do
     describe 'when the source project exists' do
       it 'returns the latest commit' do
-        commit    = double(:commit, id: '123abc')
-        ci_commit = double(:ci_commit, ref: 'master')
+        commit   = double(:commit, id: '123abc')
+        pipeline = double(:ci_pipeline, ref: 'master')
 
         allow(subject).to receive(:last_commit).and_return(commit)
 
-        expect(subject.source_project).to receive(:ci_commit).
+        expect(subject.source_project).to receive(:pipeline).
           with('123abc', 'master').
-          and_return(ci_commit)
+          and_return(pipeline)
 
-        expect(subject.ci_commit).to eq(ci_commit)
+        expect(subject.pipeline).to eq(pipeline)
       end
     end
 
@@ -437,7 +492,201 @@ describe MergeRequest, models: true do
       it 'returns nil' do
         allow(subject).to receive(:source_project).and_return(nil)
 
-        expect(subject.ci_commit).to be_nil
+        expect(subject.pipeline).to be_nil
+      end
+    end
+  end
+
+  describe '#participants' do
+    let(:project) { create(:project, :public) }
+
+    let(:mr) do
+      create(:merge_request, source_project: project, target_project: project)
+    end
+
+    let!(:note1) do
+      create(:note_on_merge_request, noteable: mr, project: project, note: 'a')
+    end
+
+    let!(:note2) do
+      create(:note_on_merge_request, noteable: mr, project: project, note: 'b')
+    end
+
+    it 'includes the merge request author' do
+      expect(mr.participants).to include(mr.author)
+    end
+
+    it 'includes the authors of the notes' do
+      expect(mr.participants).to include(note1.author, note2.author)
+    end
+  end
+
+  describe 'cached counts' do
+    it 'updates when assignees change' do
+      user1 = create(:user)
+      user2 = create(:user)
+      mr = create(:merge_request, assignee: user1)
+
+      expect(user1.assigned_open_merge_request_count).to eq(1)
+      expect(user2.assigned_open_merge_request_count).to eq(0)
+
+      mr.assignee = user2
+      mr.save
+
+      expect(user1.assigned_open_merge_request_count).to eq(0)
+      expect(user2.assigned_open_merge_request_count).to eq(1)
+    end
+  end
+
+  describe '#check_if_can_be_merged' do
+    let(:project) { create(:project, only_allow_merge_if_build_succeeds: true) }
+
+    subject { create(:merge_request, source_project: project, merge_status: :unchecked) }
+
+    context 'when it is not broken and has no conflicts' do
+      it 'is marked as mergeable' do
+        allow(subject).to receive(:broken?) { false }
+        allow(project).to receive_message_chain(:repository, :can_be_merged?) { true }
+
+        expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('can_be_merged')
+      end
+    end
+
+    context 'when broken' do
+      before { allow(subject).to receive(:broken?) { true } }
+
+      it 'becomes unmergeable' do
+        expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged')
+      end
+    end
+
+    context 'when it has conflicts' do
+      before do
+        allow(subject).to receive(:broken?) { false }
+        allow(project).to receive_message_chain(:repository, :can_be_merged?) { false }
+      end
+
+      it 'becomes unmergeable' do
+        expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged')
+      end
+    end
+  end
+
+  describe '#mergeable?' do
+    let(:project) { create(:project) }
+
+    subject { create(:merge_request, source_project: project) }
+
+    it 'returns false if #mergeable_state? is false' do
+      expect(subject).to receive(:mergeable_state?) { false }
+
+      expect(subject.mergeable?).to be_falsey
+    end
+
+    it 'return true if #mergeable_state? is true and the MR #can_be_merged? is true' do
+      allow(subject).to receive(:mergeable_state?) { true }
+      expect(subject).to receive(:check_if_can_be_merged)
+      expect(subject).to receive(:can_be_merged?) { true }
+
+      expect(subject.mergeable?).to be_truthy
+    end
+  end
+
+  describe '#mergeable_state?' do
+    let(:project) { create(:project) }
+
+    subject { create(:merge_request, source_project: project) }
+
+    it 'checks if merge request can be merged' do
+      allow(subject).to receive(:mergeable_ci_state?) { true }
+      expect(subject).to receive(:check_if_can_be_merged)
+
+      subject.mergeable?
+    end
+
+    context 'when not open' do
+      before { subject.close }
+
+      it 'returns false' do
+        expect(subject.mergeable_state?).to be_falsey
+      end
+    end
+
+    context 'when working in progress' do
+      before { subject.title = 'WIP MR' }
+
+      it 'returns false' do
+        expect(subject.mergeable_state?).to be_falsey
+      end
+    end
+
+    context 'when broken' do
+      before { allow(subject).to receive(:broken?) { true } }
+
+      it 'returns false' do
+        expect(subject.mergeable_state?).to be_falsey
+      end
+    end
+
+    context 'when failed' do
+      before { allow(subject).to receive(:broken?) { false } }
+
+      context 'when project settings restrict to merge only if build succeeds and build failed' do
+        before do
+          project.only_allow_merge_if_build_succeeds = true
+          allow(subject).to receive(:mergeable_ci_state?) { false }
+        end
+
+        it 'returns false' do
+          expect(subject.mergeable_state?).to be_falsey
+        end
+      end
+    end
+  end
+
+  describe '#mergeable_ci_state?' do
+    let(:project) { create(:empty_project, only_allow_merge_if_build_succeeds: true) }
+    let(:pipeline) { create(:ci_empty_pipeline) }
+
+    subject { build(:merge_request, target_project: project) }
+
+    context 'when it is only allowed to merge when build is green' do
+      context 'and a failed pipeline is associated' do
+        before do
+          pipeline.statuses << create(:commit_status, status: 'failed', project: project)
+          allow(subject).to receive(:pipeline) { pipeline }
+        end
+
+        it { expect(subject.mergeable_ci_state?).to be_falsey }
+      end
+
+      context 'when no pipeline is associated' do
+        before do
+          allow(subject).to receive(:pipeline) { nil }
+        end
+
+        it { expect(subject.mergeable_ci_state?).to be_truthy }
+      end
+    end
+
+    context 'when merges are not restricted to green builds' do
+      subject { build(:merge_request, target_project: build(:empty_project, only_allow_merge_if_build_succeeds: false)) }
+
+      context 'and a failed pipeline is associated' do
+        before do
+          pipeline.statuses << create(:commit_status, status: 'failed', project: project)
+          allow(subject).to receive(:pipeline) { pipeline }
+        end
+
+        it { expect(subject.mergeable_ci_state?).to be_truthy }
+      end
+
+      context 'when no pipeline is associated' do
+        before do
+          allow(subject).to receive(:pipeline) { nil }
+        end
+
+        it { expect(subject.mergeable_ci_state?).to be_truthy }
       end
     end
   end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 5d916f0e6a65b2263cb506f583e033438d2f7436..285ab19cfafd9d93526ff1193bd229637b456a27 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -9,9 +9,47 @@ describe Note, models: true do
     it { is_expected.to have_many(:todos).dependent(:destroy) }
   end
 
+  describe 'modules' do
+    subject { described_class }
+
+    it { is_expected.to include_module(Participable) }
+    it { is_expected.to include_module(Mentionable) }
+    it { is_expected.to include_module(Awardable) }
+
+    it { is_expected.to include_module(Gitlab::CurrentSettings) }
+  end
+
   describe 'validation' do
     it { is_expected.to validate_presence_of(:note) }
     it { is_expected.to validate_presence_of(:project) }
+
+    context 'when note is on commit' do
+      before { allow(subject).to receive(:for_commit?).and_return(true) }
+
+      it { is_expected.to validate_presence_of(:commit_id) }
+      it { is_expected.not_to validate_presence_of(:noteable_id) }
+    end
+
+    context 'when note is not on commit' do
+      before { allow(subject).to receive(:for_commit?).and_return(false) }
+
+      it { is_expected.not_to validate_presence_of(:commit_id) }
+      it { is_expected.to validate_presence_of(:noteable_id) }
+    end
+
+    context 'when noteable and note project differ' do
+      subject do
+        build(:note, noteable: build_stubbed(:issue),
+                     project: build_stubbed(:project))
+      end
+
+      it { is_expected.to be_invalid }
+    end
+
+    context 'when noteable and note project are the same' do
+      subject { create(:note) }
+      it { is_expected.to be_valid }
+    end
   end
 
   describe "Commit notes" do
@@ -89,12 +127,23 @@ describe Note, models: true do
   end
 
   describe "#all_references" do
-    let!(:note1) { create(:note) }
-    let!(:note2) { create(:note) }
+    let!(:note1) { create(:note_on_issue) }
+    let!(:note2) { create(:note_on_issue) }
 
     it "reads the rendered note body from the cache" do
-      expect(Banzai::Renderer).to receive(:render).with(note1.note, pipeline: :note, cache_key: [note1, "note"], project: note1.project)
-      expect(Banzai::Renderer).to receive(:render).with(note2.note, pipeline: :note, cache_key: [note2, "note"], project: note2.project)
+      expect(Banzai::Renderer).to receive(:render).
+        with(note1.note,
+             pipeline: :note,
+             cache_key: [note1, "note"],
+             project: note1.project,
+             author: note1.author)
+
+      expect(Banzai::Renderer).to receive(:render).
+        with(note2.note,
+             pipeline: :note,
+             cache_key: [note2, "note"],
+             project: note2.project,
+             author: note2.author)
 
       note1.all_references
       note2.all_references
@@ -102,7 +151,7 @@ describe Note, models: true do
   end
 
   describe '.search' do
-    let(:note) { create(:note, note: 'WoW') }
+    let(:note) { create(:note_on_issue, note: 'WoW') }
 
     it 'returns notes with matching content' do
       expect(described_class.search(note.note)).to eq([note])
@@ -111,22 +160,31 @@ describe Note, models: true do
     it 'returns notes with matching content regardless of the casing' do
       expect(described_class.search('WOW')).to eq([note])
     end
-  end
 
-  describe '.grouped_awards' do
-    before do
-      create :note, note: "smile", is_award: true
-      create :note, note: "smile", is_award: true
-    end
+    context "confidential issues" do
+      let(:user) { create(:user) }
+      let(:project) { create(:project) }
+      let(:confidential_issue) { create(:issue, :confidential, project: project, author: user) }
+      let(:confidential_note) { create(:note, note: "Random", noteable: confidential_issue, project: confidential_issue.project) }
 
-    it "returns grouped hash of notes" do
-      expect(Note.grouped_awards.keys.size).to eq(3)
-      expect(Note.grouped_awards["smile"]).to match_array(Note.all)
-    end
+      it "returns notes with matching content if user can see the issue" do
+        expect(described_class.search(confidential_note.note, as_user: user)).to eq([confidential_note])
+      end
 
-    it "returns thumbsup and thumbsdown always" do
-      expect(Note.grouped_awards["thumbsup"]).to match_array(Note.none)
-      expect(Note.grouped_awards["thumbsdown"]).to match_array(Note.none)
+      it "does not return notes with matching content if user can not see the issue" do
+        user = create(:user)
+        expect(described_class.search(confidential_note.note, as_user: user)).to be_empty
+      end
+
+      it "does not return notes with matching content for project members with guest role" do
+        user = create(:user)
+        project.team << [user, :guest]
+        expect(described_class.search(confidential_note.note, as_user: user)).to be_empty
+      end
+
+      it "does not return notes with matching content for unauthenticated users" do
+        expect(described_class.search(confidential_note.note)).to be_empty
+      end
     end
   end
 
@@ -140,11 +198,6 @@ describe Note, models: true do
       note = build(:note, system: true)
       expect(note.editable?).to be_falsy
     end
-
-    it "returns false" do
-      note = build(:note, is_award: true, note: "smiley")
-      expect(note.editable?).to be_falsy
-    end
   end
 
   describe "cross_reference_not_visible_for?" do
@@ -171,23 +224,6 @@ describe Note, models: true do
     end
   end
 
-  describe "set_award!" do
-    let(:merge_request) { create :merge_request }
-
-    it "converts aliases to actual name" do
-      note = create(:note, note: ":+1:", noteable: merge_request)
-      expect(note.reload.note).to eq("thumbsup")
-    end
-
-    it "is not an award emoji when comment is on a diff" do
-      note = create(:note_on_merge_request_diff, note: ":blowfish:", noteable: merge_request, line_code: "11d5d2e667e9da4f7f610f81d86c974b146b13bd_0_2")
-      note = note.reload
-
-      expect(note.note).to eq(":blowfish:")
-      expect(note.is_award?).to be_falsy
-    end
-  end
-
   describe 'clear_blank_line_code!' do
     it 'clears a blank line code before validation' do
       note = build(:note, line_code: ' ')
@@ -195,4 +231,14 @@ describe Note, models: true do
       expect { note.valid? }.to change(note, :line_code).to(nil)
     end
   end
+
+  describe '#participants' do
+    it 'includes the note author' do
+      project = create(:project, :public)
+      issue = create(:issue, project: project)
+      note = create(:note_on_issue, noteable: issue, project: project)
+
+      expect(note.participants).to include(note.author)
+    end
+  end
 end
diff --git a/spec/models/notification_setting_spec.rb b/spec/models/notification_setting_spec.rb
index 295081e9da1646b136a85f08ed6c284738deabdc..4e24e89b00830c9cce0f6abe946262539577429a 100644
--- a/spec/models/notification_setting_spec.rb
+++ b/spec/models/notification_setting_spec.rb
@@ -10,7 +10,6 @@ RSpec.describe NotificationSetting, type: :model do
     subject { NotificationSetting.new(source_id: 1, source_type: 'Project') }
 
     it { is_expected.to validate_presence_of(:user) }
-    it { is_expected.to validate_presence_of(:source) }
     it { is_expected.to validate_presence_of(:level) }
     it { is_expected.to validate_uniqueness_of(:user_id).scoped_to([:source_id, :source_type]).with_message(/already exists in source/) }
   end
diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb
index 0b95bf594c52b0aca11192d6ced85dc02d95ac2b..0cbea5be10623ba712ad07cd1906955bd7a87311 100644
--- a/spec/models/pages_domain_spec.rb
+++ b/spec/models/pages_domain_spec.rb
@@ -23,13 +23,13 @@ describe PagesDomain, models: true do
     context 'no domain' do
       let(:domain) { nil }
 
-      it { is_expected.to_not be_valid }
+      it { is_expected.not_to be_valid }
     end
 
     context 'invalid domain' do
       let(:domain) { '0123123' }
 
-      it { is_expected.to_not be_valid }
+      it { is_expected.not_to be_valid }
     end
 
     context 'domain from .example.com' do
@@ -37,7 +37,7 @@ describe PagesDomain, models: true do
 
       before { allow(Settings.pages).to receive(:host).and_return('domain.com') }
 
-      it { is_expected.to_not be_valid }
+      it { is_expected.not_to be_valid }
     end
   end
 
@@ -47,13 +47,13 @@ describe PagesDomain, models: true do
     context 'when only certificate is specified' do
       let(:domain) { build(:pages_domain, :with_certificate) }
 
-      it { is_expected.to_not be_valid }
+      it { is_expected.not_to be_valid }
     end
 
     context 'when only key is specified' do
       let(:domain) { build(:pages_domain, :with_key) }
 
-      it { is_expected.to_not be_valid }
+      it { is_expected.not_to be_valid }
     end
 
     context 'with matching key' do
@@ -65,7 +65,7 @@ describe PagesDomain, models: true do
     context 'for not matching key' do
       let(:domain) { build(:pages_domain, :with_missing_chain, :with_key) }
 
-      it { is_expected.to_not be_valid }
+      it { is_expected.not_to be_valid }
     end
   end
 
@@ -157,6 +157,6 @@ describe PagesDomain, models: true do
     subject { domain.certificate_text }
 
     # We test only existence of output, since the output is long
-    it { is_expected.to_not be_empty }
+    it { is_expected.not_to be_empty }
   end
 end
diff --git a/spec/models/path_lock_spec.rb b/spec/models/path_lock_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1f741c5c7fcb8cee71b236c8bfebe23a89fd2b21
--- /dev/null
+++ b/spec/models/path_lock_spec.rb
@@ -0,0 +1,13 @@
+require 'spec_helper'
+
+describe PathLock, models: true do
+  let(:path_lock) { create(:path_lock) }
+
+  it { is_expected.to belong_to(:project) }
+  it { is_expected.to belong_to(:user) }
+
+  it { is_expected.to validate_presence_of(:user) }
+  it { is_expected.to validate_presence_of(:project) }
+  it { is_expected.to validate_presence_of(:path) }
+  it { is_expected.to validate_uniqueness_of(:path).scoped_to(:project_id, :user_id) }
+end
diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb
index e771f35811ec004e3e6e4af8da371addf625cf99..9ae461f8c2d8befbb6042a3b49fec7aeb455e0f9 100644
--- a/spec/models/project_services/bamboo_service_spec.rb
+++ b/spec/models/project_services/bamboo_service_spec.rb
@@ -126,25 +126,25 @@ describe BambooService, models: true do
     it 'returns a specific URL when status is 500' do
       stub_request(status: 500)
 
-      expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/browse/foo')
+      expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/bamboo/browse/foo')
     end
 
     it 'returns a specific URL when response has no results' do
       stub_request(body: %Q({"results":{"results":{"size":"0"}}}))
 
-      expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/browse/foo')
+      expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/bamboo/browse/foo')
     end
 
     it 'returns a build URL when bamboo_url has no trailing slash' do
       stub_request(body: %Q({"results":{"results":{"result":{"planResultKey":{"key":"42"}}}}}))
 
-      expect(service(bamboo_url: 'http://gitlab.com').build_page('123', 'unused')).to eq('http://gitlab.com/browse/42')
+      expect(service(bamboo_url: 'http://gitlab.com/bamboo').build_page('123', 'unused')).to eq('http://gitlab.com/bamboo/browse/42')
     end
 
     it 'returns a build URL when bamboo_url has a trailing slash' do
       stub_request(body: %Q({"results":{"results":{"result":{"planResultKey":{"key":"42"}}}}}))
 
-      expect(service(bamboo_url: 'http://gitlab.com/').build_page('123', 'unused')).to eq('http://gitlab.com/browse/42')
+      expect(service(bamboo_url: 'http://gitlab.com/bamboo/').build_page('123', 'unused')).to eq('http://gitlab.com/bamboo/browse/42')
     end
   end
 
@@ -192,9 +192,9 @@ describe BambooService, models: true do
     end
   end
 
-  def service(bamboo_url: 'http://gitlab.com')
+  def service(bamboo_url: 'http://gitlab.com/bamboo')
     described_class.create(
-      project: build_stubbed(:empty_project),
+      project: create(:empty_project),
       properties: {
         bamboo_url: bamboo_url,
         username: 'mic',
@@ -205,7 +205,7 @@ describe BambooService, models: true do
   end
 
   def stub_request(status: 200, body: nil, build_state: 'success')
-    bamboo_full_url = 'http://mic:password@gitlab.com/rest/api/latest/result?label=123&os_authType=basic'
+    bamboo_full_url = 'http://mic:password@gitlab.com/bamboo/rest/api/latest/result?label=123&os_authType=basic'
     body ||= %Q({"results":{"results":{"result":{"buildState":"#{build_state}"}}}})
 
     WebMock.stub_request(:get, bamboo_full_url).to_return(
diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb
index d1ff20b9808b99ff17ac5887dfa463ecf0888fb9..2c367bd6041fe37624b0105d29f8a2e059e8a5cf 100644
--- a/spec/models/project_services/hipchat_service_spec.rb
+++ b/spec/models/project_services/hipchat_service_spec.rb
@@ -189,86 +189,117 @@ describe HipchatService, models: true do
     context "Note events" do
       let(:user) { create(:user) }
       let(:project) { create(:project, creator_id: user.id) }
-      let(:issue)         { create(:issue, project: project) }
-      let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
-      let(:snippet)       { create(:project_snippet, project: project) }
-      let(:commit_note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'a comment on a commit') }
-      let(:merge_request_note) { create(:note_on_merge_request, noteable_id: merge_request.id, note: "merge request note") }
-      let(:issue_note) { create(:note_on_issue, noteable_id: issue.id, note: "issue note")}
-      let(:snippet_note) { create(:note_on_project_snippet, noteable_id: snippet.id, note: "snippet note") }
-
-      it "should call Hipchat API for commit comment events" do
-        data = Gitlab::NoteDataBuilder.build(commit_note, user)
-        hipchat.execute(data)
 
-        expect(WebMock).to have_requested(:post, api_url).once
+      context 'when commit comment event triggered' do
+        let(:commit_note) do
+          create(:note_on_commit, author: user, project: project,
+                                  commit_id: project.repository.commit.id,
+                                  note: 'a comment on a commit')
+        end
+
+        it "should call Hipchat API for commit comment events" do
+          data = Gitlab::NoteDataBuilder.build(commit_note, user)
+          hipchat.execute(data)
 
-        message = hipchat.send(:create_message, data)
+          expect(WebMock).to have_requested(:post, api_url).once
 
-        obj_attr = data[:object_attributes]
-        commit_id = Commit.truncate_sha(data[:commit][:id])
-        title = hipchat.send(:format_title, data[:commit][:message])
+          message = hipchat.send(:create_message, data)
 
-        expect(message).to eq("#{user.name} commented on " \
-            "<a href=\"#{obj_attr[:url]}\">commit #{commit_id}</a> in " \
-            "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
-            "#{title}" \
-            "<pre>a comment on a commit</pre>")
+          obj_attr = data[:object_attributes]
+          commit_id = Commit.truncate_sha(data[:commit][:id])
+          title = hipchat.send(:format_title, data[:commit][:message])
+
+          expect(message).to eq("#{user.name} commented on " \
+              "<a href=\"#{obj_attr[:url]}\">commit #{commit_id}</a> in " \
+              "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
+              "#{title}" \
+              "<pre>a comment on a commit</pre>")
+        end
       end
 
-      it "should call Hipchat API for merge request comment events" do
-        data = Gitlab::NoteDataBuilder.build(merge_request_note, user)
-        hipchat.execute(data)
+      context 'when merge request comment event triggered' do
+        let(:merge_request) do
+          create(:merge_request, source_project: project,
+                                 target_project: project)
+        end
 
-        expect(WebMock).to have_requested(:post, api_url).once
+        let(:merge_request_note) do
+          create(:note_on_merge_request, noteable: merge_request,
+                                         project: project,
+                                         note: "merge request note")
+        end
 
-        message = hipchat.send(:create_message, data)
+        it "should call Hipchat API for merge request comment events" do
+          data = Gitlab::NoteDataBuilder.build(merge_request_note, user)
+          hipchat.execute(data)
 
-        obj_attr = data[:object_attributes]
-        merge_id = data[:merge_request]['iid']
-        title = data[:merge_request]['title']
+          expect(WebMock).to have_requested(:post, api_url).once
 
-        expect(message).to eq("#{user.name} commented on " \
-            "<a href=\"#{obj_attr[:url]}\">merge request !#{merge_id}</a> in " \
-            "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
-            "<b>#{title}</b>" \
-            "<pre>merge request note</pre>")
+          message = hipchat.send(:create_message, data)
+
+          obj_attr = data[:object_attributes]
+          merge_id = data[:merge_request]['iid']
+          title = data[:merge_request]['title']
+
+          expect(message).to eq("#{user.name} commented on " \
+              "<a href=\"#{obj_attr[:url]}\">merge request !#{merge_id}</a> in " \
+              "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
+              "<b>#{title}</b>" \
+              "<pre>merge request note</pre>")
+        end
       end
 
-      it "should call Hipchat API for issue comment events" do
-        data = Gitlab::NoteDataBuilder.build(issue_note, user)
-        hipchat.execute(data)
+      context 'when issue comment event triggered' do
+        let(:issue) { create(:issue, project: project) }
+        let(:issue_note) do
+          create(:note_on_issue, noteable: issue, project: project,
+                                 note: "issue note")
+        end
 
-        message = hipchat.send(:create_message, data)
+        it "should call Hipchat API for issue comment events" do
+          data = Gitlab::NoteDataBuilder.build(issue_note, user)
+          hipchat.execute(data)
 
-        obj_attr = data[:object_attributes]
-        issue_id = data[:issue]['iid']
-        title = data[:issue]['title']
+          message = hipchat.send(:create_message, data)
 
-        expect(message).to eq("#{user.name} commented on " \
-            "<a href=\"#{obj_attr[:url]}\">issue ##{issue_id}</a> in " \
-            "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
-            "<b>#{title}</b>" \
-            "<pre>issue note</pre>")
+          obj_attr = data[:object_attributes]
+          issue_id = data[:issue]['iid']
+          title = data[:issue]['title']
+
+          expect(message).to eq("#{user.name} commented on " \
+              "<a href=\"#{obj_attr[:url]}\">issue ##{issue_id}</a> in " \
+              "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
+              "<b>#{title}</b>" \
+              "<pre>issue note</pre>")
+        end
       end
 
-      it "should call Hipchat API for snippet comment events" do
-        data = Gitlab::NoteDataBuilder.build(snippet_note, user)
-        hipchat.execute(data)
+      context 'when snippet comment event triggered' do
+        let(:snippet) { create(:project_snippet, project: project) }
+        let(:snippet_note) do
+          create(:note_on_project_snippet, noteable: snippet,
+                                           project: project,
+                                           note: "snippet note")
+        end
 
-        expect(WebMock).to have_requested(:post, api_url).once
+        it "should call Hipchat API for snippet comment events" do
+          data = Gitlab::NoteDataBuilder.build(snippet_note, user)
+          hipchat.execute(data)
 
-        message = hipchat.send(:create_message, data)
+          expect(WebMock).to have_requested(:post, api_url).once
 
-        obj_attr = data[:object_attributes]
-        snippet_id = data[:snippet]['id']
-        title = data[:snippet]['title']
+          message = hipchat.send(:create_message, data)
 
-        expect(message).to eq("#{user.name} commented on " \
-            "<a href=\"#{obj_attr[:url]}\">snippet ##{snippet_id}</a> in " \
-            "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
-            "<b>#{title}</b>" \
-            "<pre>snippet note</pre>")
+          obj_attr = data[:object_attributes]
+          snippet_id = data[:snippet]['id']
+          title = data[:snippet]['title']
+
+          expect(message).to eq("#{user.name} commented on " \
+              "<a href=\"#{obj_attr[:url]}\">snippet ##{snippet_id}</a> in " \
+              "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
+              "<b>#{title}</b>" \
+              "<pre>snippet note</pre>")
+        end
       end
     end
 
@@ -316,7 +347,7 @@ describe HipchatService, models: true do
         it "should notify only broken" do
           hipchat.notify_only_broken_builds = true
           hipchat.execute(data)
-          expect(WebMock).to_not have_requested(:post, api_url).once
+          expect(WebMock).not_to have_requested(:post, api_url).once
         end
       end
     end
diff --git a/spec/models/project_services/jenkins_service_spec.rb b/spec/models/project_services/jenkins_service_spec.rb
index f0273ba07ade5e76e6cec1a71c332435d7bd6901..3efaf51b36ae8373e5ebbe57e1d351512477ec5f 100644
--- a/spec/models/project_services/jenkins_service_spec.rb
+++ b/spec/models/project_services/jenkins_service_spec.rb
@@ -25,6 +25,16 @@ describe JenkinsService do
     it { is_expected.to have_one :service_hook }
   end
   let(:project) { create(:project) }
+  let(:jenkins_params) do
+    {
+      active: true,
+      project: project,
+      properties: {
+        jenkins_url: 'http://jenkins.example.com/',
+        project_name: 'my_project'
+      }
+    }
+  end
 
   describe 'username validation' do
     before do
@@ -74,19 +84,26 @@ describe JenkinsService do
     end
   end
 
+  describe '#test' do
+    it 'returns the right status' do
+      user = create(:user, username: 'username')
+      project = create(:project, name: 'project')
+      push_sample_data = Gitlab::PushDataBuilder.build_sample(project, user)
+      jenkins_service = described_class.create(jenkins_params)
+      stub_request(:post, jenkins_service.hook_url)
+
+      result = jenkins_service.test(push_sample_data)
+
+      expect(result).to eq({ success: true, result: '' })
+    end
+  end
+
   describe '#execute' do
     it 'adds default web hook headers to the request' do
       user = create(:user, username: 'username')
       project = create(:project, name: 'project')
       push_sample_data = Gitlab::PushDataBuilder.build_sample(project, user)
-      jenkins_service = described_class.create(
-        active: true,
-        project: project,
-        properties: {
-          jenkins_url: 'http://jenkins.example.com/',
-          project_name: 'my_project'
-        }
-      )
+      jenkins_service = described_class.create(jenkins_params)
       stub_request(:post, jenkins_service.hook_url)
 
       jenkins_service.execute(push_sample_data)
diff --git a/spec/models/project_services/slack_service/build_message_spec.rb b/spec/models/project_services/slack_service/build_message_spec.rb
index 621c83c0cda355a42d7485c52a814270aaf62bb6..7fcfdf0eacdd76b11b3b4a0d53278384d7fb1ca7 100644
--- a/spec/models/project_services/slack_service/build_message_spec.rb
+++ b/spec/models/project_services/slack_service/build_message_spec.rb
@@ -15,7 +15,7 @@ describe SlackService::BuildMessage do
       commit: {
         status: status,
         author_name: 'hacker',
-        duration: 10,
+        duration: duration,
       },
     }
   end
@@ -23,9 +23,10 @@ describe SlackService::BuildMessage do
   context 'succeeded' do
     let(:status) { 'success' }
     let(:color) { 'good' }
-
+    let(:duration) { 10 }
+    
     it 'returns a message with information about succeeded build' do
-      message = '<somewhere.com|project_name>: Commit <somewhere.com/commit/97de212e80737a608d939f648d959671fb0a0142/builds|97de212e> of <somewhere.com/commits/develop|develop> branch by hacker passed in 10 second(s)'
+      message = '<somewhere.com|project_name>: Commit <somewhere.com/commit/97de212e80737a608d939f648d959671fb0a0142/builds|97de212e> of <somewhere.com/commits/develop|develop> branch by hacker passed in 10 seconds'
       expect(subject.pretext).to be_empty
       expect(subject.fallback).to eq(message)
       expect(subject.attachments).to eq([text: message, color: color])
@@ -35,9 +36,23 @@ describe SlackService::BuildMessage do
   context 'failed' do
     let(:status) { 'failed' }
     let(:color) { 'danger' }
+    let(:duration) { 10 }
 
     it 'returns a message with information about failed build' do
-      message = '<somewhere.com|project_name>: Commit <somewhere.com/commit/97de212e80737a608d939f648d959671fb0a0142/builds|97de212e> of <somewhere.com/commits/develop|develop> branch by hacker failed in 10 second(s)'
+      message = '<somewhere.com|project_name>: Commit <somewhere.com/commit/97de212e80737a608d939f648d959671fb0a0142/builds|97de212e> of <somewhere.com/commits/develop|develop> branch by hacker failed in 10 seconds'
+      expect(subject.pretext).to be_empty
+      expect(subject.fallback).to eq(message)
+      expect(subject.attachments).to eq([text: message, color: color])
+    end
+  end 
+  
+  describe '#seconds_name' do
+    let(:status) { 'failed' }
+    let(:color) { 'danger' }
+    let(:duration) { 1 }
+
+    it 'returns seconds as singular when there is only one' do
+      message = '<somewhere.com|project_name>: Commit <somewhere.com/commit/97de212e80737a608d939f648d959671fb0a0142/builds|97de212e> of <somewhere.com/commits/develop|develop> branch by hacker failed in 1 second'
       expect(subject.pretext).to be_empty
       expect(subject.fallback).to eq(message)
       expect(subject.attachments).to eq([text: message, color: color])
diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb
index a97b7560137fdaec9860916a2926690bf2b71341..155f3e74e0d24989c51bdb357f494927477d481a 100644
--- a/spec/models/project_services/slack_service_spec.rb
+++ b/spec/models/project_services/slack_service_spec.rb
@@ -142,13 +142,6 @@ describe SlackService, models: true do
     let(:slack)   { SlackService.new }
     let(:user) { create(:user) }
     let(:project) { create(:project, creator_id: user.id) }
-    let(:issue)         { create(:issue, project: project) }
-    let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
-    let(:snippet)       { create(:project_snippet, project: project) }
-    let(:commit_note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'a comment on a commit') }
-    let(:merge_request_note) { create(:note_on_merge_request, noteable_id: merge_request.id, note: "merge request note") }
-    let(:issue_note) { create(:note_on_issue, noteable_id: issue.id, note: "issue note")}
-    let(:snippet_note) { create(:note_on_project_snippet, noteable_id: snippet.id, note: "snippet note") }
     let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' }
 
     before do
@@ -162,32 +155,61 @@ describe SlackService, models: true do
       WebMock.stub_request(:post, webhook_url)
     end
 
-    it "should call Slack API for commit comment events" do
-      data = Gitlab::NoteDataBuilder.build(commit_note, user)
-      slack.execute(data)
+    context 'when commit comment event executed' do
+      let(:commit_note) do
+        create(:note_on_commit, author: user,
+                                project: project,
+                                commit_id: project.repository.commit.id,
+                                note: 'a comment on a commit')
+      end
 
-      expect(WebMock).to have_requested(:post, webhook_url).once
+      it "should call Slack API for commit comment events" do
+        data = Gitlab::NoteDataBuilder.build(commit_note, user)
+        slack.execute(data)
+
+        expect(WebMock).to have_requested(:post, webhook_url).once
+      end
     end
 
-    it "should call Slack API for merge request comment events" do
-      data = Gitlab::NoteDataBuilder.build(merge_request_note, user)
-      slack.execute(data)
+    context 'when merge request comment event executed' do
+      let(:merge_request_note) do
+        create(:note_on_merge_request, project: project,
+                                       note: "merge request note")
+      end
 
-      expect(WebMock).to have_requested(:post, webhook_url).once
+      it "should call Slack API for merge request comment events" do
+        data = Gitlab::NoteDataBuilder.build(merge_request_note, user)
+        slack.execute(data)
+
+        expect(WebMock).to have_requested(:post, webhook_url).once
+      end
     end
 
-    it "should call Slack API for issue comment events" do
-      data = Gitlab::NoteDataBuilder.build(issue_note, user)
-      slack.execute(data)
+    context 'when issue comment event executed' do
+      let(:issue_note) do
+        create(:note_on_issue, project: project, note: "issue note")
+      end
 
-      expect(WebMock).to have_requested(:post, webhook_url).once
+      it "should call Slack API for issue comment events" do
+        data = Gitlab::NoteDataBuilder.build(issue_note, user)
+        slack.execute(data)
+
+        expect(WebMock).to have_requested(:post, webhook_url).once
+      end
     end
 
-    it "should call Slack API for snippet comment events" do
-      data = Gitlab::NoteDataBuilder.build(snippet_note, user)
-      slack.execute(data)
+    context 'when snippet comment event executed' do
+      let(:snippet_note) do
+        create(:note_on_project_snippet, project: project,
+                                         note: "snippet note")
+      end
 
-      expect(WebMock).to have_requested(:post, webhook_url).once
+      it "should call Slack API for snippet comment events" do
+        data = Gitlab::NoteDataBuilder.build(snippet_note, user)
+        slack.execute(data)
+
+        expect(WebMock).to have_requested(:post, webhook_url).once
+      end
     end
   end
 end
diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb
index ad24b895170cb2531cb55937b0456c7e4779d5cd..474715d24c3adbcd7cde80b017bba2aa72d9f5b5 100644
--- a/spec/models/project_services/teamcity_service_spec.rb
+++ b/spec/models/project_services/teamcity_service_spec.rb
@@ -126,19 +126,19 @@ describe TeamcityService, models: true do
     it 'returns a specific URL when status is 500' do
       stub_request(status: 500)
 
-      expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/viewLog.html?buildTypeId=foo')
+      expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/teamcity/viewLog.html?buildTypeId=foo')
     end
 
     it 'returns a build URL when teamcity_url has no trailing slash' do
       stub_request(body: %Q({"build":{"id":"666"}}))
 
-      expect(service(teamcity_url: 'http://gitlab.com').build_page('123', 'unused')).to eq('http://gitlab.com/viewLog.html?buildId=666&buildTypeId=foo')
+      expect(service(teamcity_url: 'http://gitlab.com/teamcity').build_page('123', 'unused')).to eq('http://gitlab.com/teamcity/viewLog.html?buildId=666&buildTypeId=foo')
     end
 
     it 'returns a build URL when teamcity_url has a trailing slash' do
       stub_request(body: %Q({"build":{"id":"666"}}))
 
-      expect(service(teamcity_url: 'http://gitlab.com/').build_page('123', 'unused')).to eq('http://gitlab.com/viewLog.html?buildId=666&buildTypeId=foo')
+      expect(service(teamcity_url: 'http://gitlab.com/teamcity/').build_page('123', 'unused')).to eq('http://gitlab.com/teamcity/viewLog.html?buildId=666&buildTypeId=foo')
     end
   end
 
@@ -180,9 +180,9 @@ describe TeamcityService, models: true do
     end
   end
 
-  def service(teamcity_url: 'http://gitlab.com')
+  def service(teamcity_url: 'http://gitlab.com/teamcity')
     described_class.create(
-      project: build_stubbed(:empty_project),
+      project: create(:empty_project),
       properties: {
         teamcity_url: teamcity_url,
         username: 'mic',
@@ -193,7 +193,7 @@ describe TeamcityService, models: true do
   end
 
   def stub_request(status: 200, body: nil, build_status: 'success')
-    teamcity_full_url = 'http://mic:password@gitlab.com/httpAuth/app/rest/builds/branch:unspecified:any,number:123'
+    teamcity_full_url = 'http://mic:password@gitlab.com/teamcity/httpAuth/app/rest/builds/branch:unspecified:any,number:123'
     body ||= %Q({"build":{"status":"#{build_status}","id":"666"}})
 
     WebMock.stub_request(:get, teamcity_full_url).to_return(
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 0b666b0c2ae1ece8ed08a4a199f30801b14a0840..88d3ad3ee9da7b7a7c20b638e39e6c6c873b17c6 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -22,14 +22,17 @@ describe Project, models: true do
     it { is_expected.to have_one(:pushover_service).dependent(:destroy) }
     it { is_expected.to have_one(:asana_service).dependent(:destroy) }
     it { is_expected.to have_many(:commit_statuses) }
-    it { is_expected.to have_many(:ci_commits) }
+    it { is_expected.to have_many(:pipelines) }
     it { is_expected.to have_many(:builds) }
     it { is_expected.to have_many(:runner_projects) }
     it { is_expected.to have_many(:runners) }
     it { is_expected.to have_many(:variables) }
     it { is_expected.to have_many(:triggers) }
     it { is_expected.to have_many(:pages_domains) }
+    it { is_expected.to have_many(:environments).dependent(:destroy) }
+    it { is_expected.to have_many(:deployments).dependent(:destroy) }
     it { is_expected.to have_many(:todos).dependent(:destroy) }
+    it { is_expected.to have_many(:path_locks).dependent(:destroy) }
   end
 
   describe 'modules' do
@@ -54,14 +57,13 @@ describe Project, models: true do
     it { is_expected.to validate_length_of(:path).is_within(0..255) }
     it { is_expected.to validate_length_of(:description).is_within(0..2000) }
     it { is_expected.to validate_presence_of(:creator) }
-    it { is_expected.to validate_length_of(:issues_tracker_id).is_within(0..255) }
     it { is_expected.to validate_presence_of(:namespace) }
 
     it 'should not allow new projects beyond user limits' do
       project2 = build(:project)
       allow(project2).to receive(:creator).and_return(double(can_create_project?: false, projects_limit: 0).as_null_object)
       expect(project2).not_to be_valid
-      expect(project2.errors[:limit_reached].first).to match(/Your project limit is 0/)
+      expect(project2.errors[:limit_reached].first).to match(/Personal project creation is not allowed/)
     end
   end
 
@@ -91,11 +93,17 @@ describe Project, models: true do
     it { is_expected.to respond_to(:repo_exists?) }
     it { is_expected.to respond_to(:update_merge_requests) }
     it { is_expected.to respond_to(:execute_hooks) }
-    it { is_expected.to respond_to(:name_with_namespace) }
     it { is_expected.to respond_to(:owner) }
     it { is_expected.to respond_to(:path_with_namespace) }
   end
 
+  describe '#name_with_namespace' do
+    let(:project) { build_stubbed(:empty_project) }
+
+    it { expect(project.name_with_namespace).to eq "#{project.namespace.human_name} / #{project.name}" }
+    it { expect(project.human_name).to eq project.name_with_namespace }
+  end
+
   describe '#to_reference' do
     let(:project) { create(:empty_project) }
 
@@ -267,24 +275,66 @@ describe Project, models: true do
     end
   end
 
-  describe :can_have_issues_tracker_id? do
+  describe :external_issue_tracker do
     let(:project) { create(:project) }
     let(:ext_project) { create(:redmine_project) }
 
-    it 'should be true for projects with external issues tracker if issues enabled' do
-      expect(ext_project.can_have_issues_tracker_id?).to be_truthy
+    context 'on existing projects with no value for has_external_issue_tracker' do
+      before(:each) do
+        project.update_column(:has_external_issue_tracker, nil)
+        ext_project.update_column(:has_external_issue_tracker, nil)
+      end
+
+      it 'updates the has_external_issue_tracker boolean' do
+        expect do
+          project.external_issue_tracker
+        end.to change { project.reload.has_external_issue_tracker }.to(false)
+
+        expect do
+          ext_project.external_issue_tracker
+        end.to change { ext_project.reload.has_external_issue_tracker }.to(true)
+      end
+    end
+
+    it 'returns nil and does not query services when there is no external issue tracker' do
+      project.build_missing_services
+      project.reload
+
+      expect(project).not_to receive(:services)
+
+      expect(project.external_issue_tracker).to eq(nil)
     end
 
-    it 'should be false for projects with internal issue tracker if issues enabled' do
-      expect(project.can_have_issues_tracker_id?).to be_falsey
+    it 'retrieves external_issue_tracker querying services and cache it when there is external issue tracker' do
+      ext_project.reload # Factory returns a project with changed attributes
+      ext_project.build_missing_services
+      ext_project.reload
+
+      expect(ext_project).to receive(:services).once.and_call_original
+
+      2.times { expect(ext_project.external_issue_tracker).to be_a_kind_of(RedmineService) }
+    end
+  end
+
+  describe :cache_has_external_issue_tracker do
+    let(:project) { create(:project) }
+
+    it 'stores true if there is any external_issue_tracker' do
+      services = double(:service, external_issue_trackers: [RedmineService.new])
+      expect(project).to receive(:services).and_return(services)
+
+      expect do
+        project.cache_has_external_issue_tracker
+      end.to change { project.has_external_issue_tracker}.to(true)
     end
 
-    it 'should be always false if issues disabled' do
-      project.issues_enabled = false
-      ext_project.issues_enabled = false
+    it 'stores false if there is no external_issue_tracker' do
+      services = double(:service, external_issue_trackers: [])
+      expect(project).to receive(:services).and_return(services)
 
-      expect(project.can_have_issues_tracker_id?).to be_falsey
-      expect(ext_project.can_have_issues_tracker_id?).to be_falsey
+      expect do
+        project.cache_has_external_issue_tracker
+      end.to change { project.has_external_issue_tracker}.to(false)
     end
   end
 
@@ -438,23 +488,23 @@ describe Project, models: true do
     end
   end
 
-  describe :ci_commit do
+  describe :pipeline do
     let(:project) { create :project }
-    let(:commit) { create :ci_commit, project: project, ref: 'master' }
+    let(:pipeline) { create :ci_pipeline, project: project, ref: 'master' }
 
-    subject { project.ci_commit(commit.sha, 'master') }
+    subject { project.pipeline(pipeline.sha, 'master') }
 
-    it { is_expected.to eq(commit) }
+    it { is_expected.to eq(pipeline) }
 
     context 'return latest' do
-      let(:commit2) { create :ci_commit, project: project, ref: 'master' }
+      let(:pipeline2) { create :ci_pipeline, project: project, ref: 'master' }
 
       before do
-        commit
-        commit2
+        pipeline
+        pipeline2
       end
 
-      it { is_expected.to eq(commit2) }
+      it { is_expected.to eq(pipeline2) }
     end
   end
 
@@ -889,6 +939,15 @@ describe Project, models: true do
     end
   end
 
+  describe '#container_registry_path_with_namespace' do
+    let(:project) { create(:empty_project, path: 'PROJECT') }
+
+    subject { project.container_registry_path_with_namespace }
+
+    it { is_expected.not_to eq(project.path_with_namespace) }
+    it { is_expected.to eq(project.path_with_namespace.downcase) }
+  end
+
   describe '#container_registry_repository' do
     let(:project) { create(:empty_project) }
 
@@ -896,7 +955,7 @@ describe Project, models: true do
 
     subject { project.container_registry_repository }
 
-    it { is_expected.to_not be_nil }
+    it { is_expected.not_to be_nil }
   end
 
   describe '#container_registry_repository_url' do
@@ -914,7 +973,7 @@ describe Project, models: true do
         }
       end
 
-      it { is_expected.to_not be_nil }
+      it { is_expected.not_to be_nil }
     end
 
     context 'for disabled registry' do
@@ -988,4 +1047,52 @@ describe Project, models: true do
       expect(project.reload.import_status).to eq('finished')
     end
   end
+
+  describe '.where_paths_in' do
+    context 'without any paths' do
+      it 'returns an empty relation' do
+        expect(Project.where_paths_in([])).to eq([])
+      end
+    end
+
+    context 'without any valid paths' do
+      it 'returns an empty relation' do
+        expect(Project.where_paths_in(%w[foo])).to eq([])
+      end
+    end
+
+    context 'with valid paths' do
+      let!(:project1) { create(:project) }
+      let!(:project2) { create(:project) }
+
+      it 'returns the projects matching the paths' do
+        projects = Project.where_paths_in([project1.path_with_namespace,
+                                           project2.path_with_namespace])
+
+        expect(projects).to contain_exactly(project1, project2)
+      end
+
+      it 'returns projects regardless of the casing of paths' do
+        projects = Project.where_paths_in([project1.path_with_namespace.upcase,
+                                           project2.path_with_namespace.upcase])
+
+        expect(projects).to contain_exactly(project1, project2)
+      end
+    end
+  end
+
+  describe '#path_lock_info' do
+    let(:project) { create :empty_project }
+    let(:path_lock) { create :path_lock, project: project }
+    let(:path) { path_lock.path }
+
+    it 'returns path_lock' do
+      expect(project.path_lock_info(path)).to eq(path_lock)
+    end
+
+    it 'returns nil' do
+      expect(project.path_lock_info('app/controllers')).to be_falsey
+    end
+
+  end
 end
diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb
index bacb17a8883646a8ffefb2f899b1d83efbbfafc3..9262aeb6ed890a074f2971af20509474711e9b01 100644
--- a/spec/models/project_team_spec.rb
+++ b/spec/models/project_team_spec.rb
@@ -29,6 +29,9 @@ describe ProjectTeam, models: true do
       it { expect(project.team.master?(nonmember)).to be_falsey }
       it { expect(project.team.member?(nonmember)).to be_falsey }
       it { expect(project.team.member?(guest)).to be_truthy }
+      it { expect(project.team.member?(reporter, Gitlab::Access::REPORTER)).to be_truthy }
+      it { expect(project.team.member?(guest, Gitlab::Access::REPORTER)).to be_falsey }
+      it { expect(project.team.member?(nonmember, Gitlab::Access::GUEST)).to be_falsey }
     end
   end
 
@@ -64,50 +67,48 @@ describe ProjectTeam, models: true do
       it { expect(project.team.master?(nonmember)).to be_falsey }
       it { expect(project.team.member?(nonmember)).to be_falsey }
       it { expect(project.team.member?(guest)).to be_truthy }
+      it { expect(project.team.member?(guest, Gitlab::Access::MASTER)).to be_truthy }
+      it { expect(project.team.member?(reporter, Gitlab::Access::MASTER)).to be_falsey }
+      it { expect(project.team.member?(nonmember, Gitlab::Access::GUEST)).to be_falsey }
     end
   end
 
-  describe :max_invited_level do
-    let(:group) { create(:group) }
-    let(:project) { create(:empty_project) }
-
-    before do
-      project.project_group_links.create(
-        group: group,
-        group_access: Gitlab::Access::DEVELOPER
-      )
-
-      group.add_user(master, Gitlab::Access::MASTER)
-      group.add_user(reporter, Gitlab::Access::REPORTER)
-    end
-
-    it { expect(project.team.max_invited_level(master.id)).to eq(Gitlab::Access::DEVELOPER) }
-    it { expect(project.team.max_invited_level(reporter.id)).to eq(Gitlab::Access::REPORTER) }
-    it { expect(project.team.max_invited_level(nonmember.id)).to be_nil }
-  end
-
-  describe :max_member_access do
-    let(:group) { create(:group) }
-    let(:project) { create(:empty_project) }
-
-    before do
-      project.project_group_links.create(
-        group: group,
-        group_access: Gitlab::Access::DEVELOPER
-      )
-
-      group.add_user(master, Gitlab::Access::MASTER)
-      group.add_user(reporter, Gitlab::Access::REPORTER)
+  describe '#find_member' do
+    context 'personal project' do
+      let(:project) { create(:empty_project) }
+      let(:requester) { create(:user) }
+
+      before do
+        project.team << [master, :master]
+        project.team << [reporter, :reporter]
+        project.team << [guest, :guest]
+        project.request_access(requester)
+      end
+
+      it { expect(project.team.find_member(master.id)).to be_a(ProjectMember) }
+      it { expect(project.team.find_member(reporter.id)).to be_a(ProjectMember) }
+      it { expect(project.team.find_member(guest.id)).to be_a(ProjectMember) }
+      it { expect(project.team.find_member(nonmember.id)).to be_nil }
+      it { expect(project.team.find_member(requester.id)).to be_nil }
     end
 
-    it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::DEVELOPER) }
-    it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::REPORTER) }
-    it { expect(project.team.max_member_access(nonmember.id)).to be_nil }
-
-    it "does not have an access" do
-      project.namespace.update(share_with_group_lock: true)
-      expect(project.team.max_member_access(master.id)).to be_nil
-      expect(project.team.max_member_access(reporter.id)).to be_nil
+    context 'group project' do
+      let(:group) { create(:group) }
+      let(:project) { create(:empty_project, group: group) }
+      let(:requester) { create(:user) }
+
+      before do
+        group.add_master(master)
+        group.add_reporter(reporter)
+        group.add_guest(guest)
+        group.request_access(requester)
+      end
+
+      it { expect(project.team.find_member(master.id)).to be_a(GroupMember) }
+      it { expect(project.team.find_member(reporter.id)).to be_a(GroupMember) }
+      it { expect(project.team.find_member(guest.id)).to be_a(GroupMember) }
+      it { expect(project.team.find_member(nonmember.id)).to be_nil }
+      it { expect(project.team.find_member(requester.id)).to be_nil }
     end
   end
 
@@ -132,4 +133,69 @@ describe ProjectTeam, models: true do
       expect(project.team.human_max_access(user.id)).to eq 'Owner'
     end
   end
+
+  describe '#max_member_access' do
+    let(:requester) { create(:user) }
+
+    context 'personal project' do
+      let(:project) { create(:empty_project) }
+
+      context 'when project is not shared with group' do
+        before do
+          project.team << [master, :master]
+          project.team << [reporter, :reporter]
+          project.team << [guest, :guest]
+          project.request_access(requester)
+        end
+
+        it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::MASTER) }
+        it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::REPORTER) }
+        it { expect(project.team.max_member_access(guest.id)).to eq(Gitlab::Access::GUEST) }
+        it { expect(project.team.max_member_access(nonmember.id)).to be_nil }
+        it { expect(project.team.max_member_access(requester.id)).to be_nil }
+      end
+
+      context 'when project is shared with group' do
+        before do
+          group = create(:group)
+          project.project_group_links.create(
+            group: group,
+            group_access: Gitlab::Access::DEVELOPER)
+
+          group.add_master(master)
+          group.add_reporter(reporter)
+        end
+
+        it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::DEVELOPER) }
+        it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::REPORTER) }
+        it { expect(project.team.max_member_access(nonmember.id)).to be_nil }
+        it { expect(project.team.max_member_access(requester.id)).to be_nil }
+
+        context 'but share_with_group_lock is true' do
+          before { project.namespace.update(share_with_group_lock: true) }
+
+          it { expect(project.team.max_member_access(master.id)).to be_nil }
+          it { expect(project.team.max_member_access(reporter.id)).to be_nil }
+        end
+      end
+    end
+
+    context 'group project' do
+      let(:group) { create(:group) }
+      let(:project) { create(:empty_project, group: group) }
+
+      before do
+        group.add_master(master)
+        group.add_reporter(reporter)
+        group.add_guest(guest)
+        group.request_access(requester)
+      end
+
+      it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::MASTER) }
+      it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::REPORTER) }
+      it { expect(project.team.max_member_access(guest.id)).to eq(Gitlab::Access::GUEST) }
+      it { expect(project.team.max_member_access(nonmember.id)).to be_nil }
+      it { expect(project.team.max_member_access(requester.id)).to be_nil }
+    end
+  end
 end
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index 91ebb612baaf5881a0caf89a9c1776f19de486c5..58b57bd4fef4dc94d1eecd60f2d6f4ae33f6c5c3 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -16,6 +16,12 @@ describe ProjectWiki, models: true do
     end
   end
 
+  describe '#web_url' do
+    it 'returns the full web URL to the wiki' do
+      expect(subject.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/wikis/home")
+    end
+  end
+
   describe "#url_to_repo" do
     it "returns the correct ssh url to the repo" do
       expect(subject.url_to_repo).to eq(gitlab_shell.url_to_repo(subject.path_with_namespace))
@@ -257,6 +263,13 @@ describe ProjectWiki, models: true do
     end
   end
 
+  describe '#hook_attrs' do
+    it 'returns a hash with values' do
+      expect(subject.hook_attrs).to be_a Hash
+      expect(subject.hook_attrs.keys).to contain_exactly(:web_url, :git_ssh_url, :git_http_url, :path_with_namespace, :default_branch)
+    end
+  end
+
   private
 
   def create_temp_repo(path)
diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb
index 4a3859ed941fe84f33361ecf7927e0c4602f923c..fa21bf76056082769e3a3f052995728ac4a6b097 100644
--- a/spec/models/remote_mirror_spec.rb
+++ b/spec/models/remote_mirror_spec.rb
@@ -1,3 +1,22 @@
+# == Schema Information
+#
+# Table name: remote_mirrors
+#
+#  id                         :integer          not null, primary key
+#  project_id                 :integer
+#  url                        :string
+#  enabled                    :boolean          default(TRUE)
+#  update_status              :string
+#  last_update_at             :datetime
+#  last_successful_update_at  :datetime
+#  last_error                 :string
+#  encrypted_credentials      :text
+#  encrypted_credentials_iv   :string
+#  encrypted_credentials_salt :string
+#  created_at                 :datetime         not null
+#  updated_at                 :datetime         not null
+#
+
 require 'rails_helper'
 
 describe RemoteMirror do
@@ -44,12 +63,22 @@ describe RemoteMirror do
         expect(mirror.credentials).to eq({ user: 'foo', password: 'bar' })
       end
 
+      it 'should update the remote config if credentials changed' do
+        mirror = create_mirror_with_url('http://foo:bar@test.com')
+        repo = mirror.project.repository
+
+        mirror.update_attribute(:url, 'http://foo:baz@test.com')
+
+        expect(repo.config["remote.#{mirror.ref_name}.url"]).to eq('http://foo:baz@test.com')
+      end
+
       it 'should still be picked up by the worker if is stuck' do
         mirror = create_mirror_with_url('http://test.com')
         mirror.update_attribute(:update_status, 'started')
 
         # this will reset some of the updated_at fields
         mirror.update_attribute(:url, 'http://foo:bar@test.com')
+
         expect(RemoteMirror.stuck.last).to eq(mirror)
       end
     end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index bfe0066752c10b70aec803d03851498748f7a771..418d39e29546c8b2ca5353f48da277d961c53994 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -100,6 +100,12 @@ describe Repository, models: true do
       expect(results.first).not_to start_with('fatal:')
     end
 
+    it 'properly handles an unmatched parenthesis' do
+      results = repository.search_files("test(", 'master')
+
+      expect(results.first).not_to start_with('fatal:')
+    end
+
     describe 'result' do
       subject { results.first }
 
@@ -437,7 +443,7 @@ describe Repository, models: true do
       end
 
       it 'does nothing' do
-        expect(repository.raw_repository).to_not receive(:autocrlf=).
+        expect(repository.raw_repository).not_to receive(:autocrlf=).
           with(:input)
 
         repository.update_autocrlf_option
@@ -505,7 +511,7 @@ describe Repository, models: true do
 
     it 'does not expire the emptiness caches for a non-empty repository' do
       expect(repository).to receive(:empty?).and_return(false)
-      expect(repository).to_not receive(:expire_emptiness_caches)
+      expect(repository).not_to receive(:expire_emptiness_caches)
 
       repository.expire_cache
     end
@@ -668,7 +674,7 @@ describe Repository, models: true do
       end
 
       it 'does not flush caches that depend on repository data' do
-        expect(repository).to_not receive(:expire_cache)
+        expect(repository).not_to receive(:expire_cache)
 
         repository.before_delete
       end
@@ -794,11 +800,13 @@ describe Repository, models: true do
 
   describe "Elastic search", elastic: true do
     before do
+      stub_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
       Repository.__elasticsearch__.create_index!
     end
 
     after do
       Repository.__elasticsearch__.delete_index!
+      stub_application_setting(elasticsearch_search: false, elasticsearch_indexing: false)
     end
 
     describe :find_commits_by_message_with_elastic do
@@ -869,18 +877,6 @@ describe Repository, models: true do
     end
   end
 
-  describe "#main_language" do
-    it 'shows the main language of the project' do
-      expect(repository.main_language).to eq("Ruby")
-    end
-
-    it 'returns nil when the repository is empty' do
-      allow(repository).to receive(:empty?).and_return(true)
-
-      expect(repository.main_language).to be_nil
-    end
-  end
-
   describe '#before_remove_tag' do
     it 'flushes the tag cache' do
       expect(repository).to receive(:expire_tag_count_cache)
@@ -991,7 +987,7 @@ describe Repository, models: true do
 
       expect(repository.avatar).to eq('logo.png')
 
-      expect(repository).to_not receive(:blob_at_branch)
+      expect(repository).not_to receive(:blob_at_branch)
       expect(repository.avatar).to eq('logo.png')
     end
   end
@@ -1085,7 +1081,7 @@ describe Repository, models: true do
         and_return(true)
 
       repository.cache_keys.each do |key|
-        expect(repository).to_not receive(key)
+        expect(repository).not_to receive(key)
       end
 
       repository.build_cache
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index 41dda5c240f106e67cf9a088ea3d17c1e213b88c..9905808e8c5669b8b0f94e8b7c23754fe9e76301 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -208,4 +208,37 @@ describe Service, models: true do
       expect(service.bamboo_url_was).to be_nil
     end
   end
+
+  describe "callbacks" do
+    let(:project) { create(:project) }
+    let!(:service) do
+      RedmineService.new(
+        project: project,
+        active: true,
+        properties: {
+          project_url: 'http://redmine/projects/project_name_in_redmine',
+          issues_url: "http://redmine/#{project.id}/project_name_in_redmine/:id",
+          new_issue_url: 'http://redmine/projects/project_name_in_redmine/issues/new'
+        }
+      )
+    end
+
+    describe "on create" do
+      it "updates the has_external_issue_tracker boolean" do
+        expect do
+          service.save!
+        end.to change { service.project.has_external_issue_tracker }.from(nil).to(true)
+      end
+    end
+
+    describe "on update" do
+      it "updates the has_external_issue_tracker boolean" do
+        service.save!
+
+        expect do
+          service.update_attributes(active: false)
+        end.to change { service.project.has_external_issue_tracker }.from(true).to(false)
+      end
+    end
+  end
 end
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index 7a613e360d4944c3423aa10e3a1ede0b573f8a9a..789816bf2c799d572612ef5f4dd7172dd22f6406 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -87,4 +87,31 @@ describe Snippet, models: true do
       expect(described_class.search_code('FOO')).to eq([snippet])
     end
   end
+
+  describe '#participants' do
+    let(:project) { create(:project, :public) }
+    let(:snippet) { create(:snippet, content: 'foo', project: project) }
+
+    let!(:note1) do
+      create(:note_on_project_snippet,
+             noteable: snippet,
+             project: project,
+             note: 'a')
+    end
+
+    let!(:note2) do
+      create(:note_on_project_snippet,
+             noteable: snippet,
+             project: project,
+             note: 'b')
+    end
+
+    it 'includes the snippet author' do
+      expect(snippet.participants).to include(snippet.author)
+    end
+
+    it 'includes the note authors' do
+      expect(snippet.participants).to include(note1.author, note2.author)
+    end
+  end
 end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index a2336f984187479bff6ee30bcc60add5972a2e16..78bb0e07895a3c4e574302d53efdca4912dc504c 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -30,6 +30,8 @@ describe User, models: true do
     it { is_expected.to have_one(:abuse_report) }
     it { is_expected.to have_many(:spam_logs).dependent(:destroy) }
     it { is_expected.to have_many(:todos).dependent(:destroy) }
+    it { is_expected.to have_many(:award_emoji).dependent(:destroy) }
+    it { is_expected.to have_many(:path_locks).dependent(:destroy) }
   end
 
   describe 'validations' do
@@ -67,7 +69,10 @@ describe User, models: true do
 
     describe 'email' do
       context 'when no signup domains listed' do
-        before { allow(current_application_settings).to receive(:restricted_signup_domains).and_return([]) }
+        before do
+          allow_any_instance_of(ApplicationSetting).to receive(:restricted_signup_domains).and_return([])
+        end
+
         it 'accepts any email' do
           user = build(:user, email: "info@example.com")
           expect(user).to be_valid
@@ -75,7 +80,10 @@ describe User, models: true do
       end
 
       context 'when a signup domain is listed and subdomains are allowed' do
-        before { allow(current_application_settings).to receive(:restricted_signup_domains).and_return(['example.com', '*.example.com']) }
+        before do
+          allow_any_instance_of(ApplicationSetting).to receive(:restricted_signup_domains).and_return(['example.com', '*.example.com'])
+        end
+
         it 'accepts info@example.com' do
           user = build(:user, email: "info@example.com")
           expect(user).to be_valid
@@ -93,7 +101,9 @@ describe User, models: true do
       end
 
       context 'when a signup domain is listed and subdomains are not allowed' do
-        before { allow(current_application_settings).to receive(:restricted_signup_domains).and_return(['example.com']) }
+        before do
+          allow_any_instance_of(ApplicationSetting).to receive(:restricted_signup_domains).and_return(['example.com'])
+        end
 
         it 'accepts info@example.com' do
           user = build(:user, email: "info@example.com")
@@ -133,6 +143,66 @@ describe User, models: true do
     end
   end
 
+  describe "scopes" do
+    describe ".with_two_factor" do
+      it "returns users with 2fa enabled via OTP" do
+        user_with_2fa = create(:user, :two_factor_via_otp)
+        user_without_2fa = create(:user)
+        users_with_two_factor = User.with_two_factor.pluck(:id)
+
+        expect(users_with_two_factor).to include(user_with_2fa.id)
+        expect(users_with_two_factor).not_to include(user_without_2fa.id)
+      end
+
+      it "returns users with 2fa enabled via U2F" do
+        user_with_2fa = create(:user, :two_factor_via_u2f)
+        user_without_2fa = create(:user)
+        users_with_two_factor = User.with_two_factor.pluck(:id)
+
+        expect(users_with_two_factor).to include(user_with_2fa.id)
+        expect(users_with_two_factor).not_to include(user_without_2fa.id)
+      end
+
+      it "returns users with 2fa enabled via OTP and U2F" do
+        user_with_2fa = create(:user, :two_factor_via_otp, :two_factor_via_u2f)
+        user_without_2fa = create(:user)
+        users_with_two_factor = User.with_two_factor.pluck(:id)
+
+        expect(users_with_two_factor).to eq([user_with_2fa.id])
+        expect(users_with_two_factor).not_to include(user_without_2fa.id)
+      end
+    end
+
+    describe ".without_two_factor" do
+      it "excludes users with 2fa enabled via OTP" do
+        user_with_2fa = create(:user, :two_factor_via_otp)
+        user_without_2fa = create(:user)
+        users_without_two_factor = User.without_two_factor.pluck(:id)
+
+        expect(users_without_two_factor).to include(user_without_2fa.id)
+        expect(users_without_two_factor).not_to include(user_with_2fa.id)
+      end
+
+      it "excludes users with 2fa enabled via U2F" do
+        user_with_2fa = create(:user, :two_factor_via_u2f)
+        user_without_2fa = create(:user)
+        users_without_two_factor = User.without_two_factor.pluck(:id)
+
+        expect(users_without_two_factor).to include(user_without_2fa.id)
+        expect(users_without_two_factor).not_to include(user_with_2fa.id)
+      end
+
+      it "excludes users with 2fa enabled via OTP and U2F" do
+        user_with_2fa = create(:user, :two_factor_via_otp, :two_factor_via_u2f)
+        user_without_2fa = create(:user)
+        users_without_two_factor = User.without_two_factor.pluck(:id)
+
+        expect(users_without_two_factor).to include(user_without_2fa.id)
+        expect(users_without_two_factor).not_to include(user_with_2fa.id)
+      end
+    end
+  end
+
   describe "Respond to" do
     it { is_expected.to respond_to(:is_admin?) }
     it { is_expected.to respond_to(:name) }
@@ -154,7 +224,10 @@ describe User, models: true do
   end
 
   describe '#confirm' do
-    before { allow(current_application_settings).to receive(:send_user_confirmation_email).and_return(true) }
+    before do
+      allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(true)
+    end
+
     let(:user) { create(:user, confirmed_at: nil, unconfirmed_email: 'test@gitlab.com') }
 
     it 'returns unconfirmed' do
@@ -818,6 +891,75 @@ describe User, models: true do
     it { is_expected.to eq([private_project]) }
   end
 
+  describe '#ci_authorized_runners' do
+    let(:user) { create(:user) }
+    let(:runner) { create(:ci_runner) }
+
+    before do
+      project.runners << runner
+    end
+
+    context 'without any projects' do
+      let(:project) { create(:project) }
+
+      it 'does not load' do
+        expect(user.ci_authorized_runners).to be_empty
+      end
+    end
+
+    context 'with personal projects runners' do
+      let(:namespace) { create(:namespace, owner: user) }
+      let(:project) { create(:project, namespace: namespace) }
+
+      it 'loads' do
+        expect(user.ci_authorized_runners).to contain_exactly(runner)
+      end
+    end
+
+    shared_examples :member do
+      context 'when the user is a master' do
+        before do
+          add_user(Gitlab::Access::MASTER)
+        end
+
+        it 'loads' do
+          expect(user.ci_authorized_runners).to contain_exactly(runner)
+        end
+      end
+
+      context 'when the user is a developer' do
+        before do
+          add_user(Gitlab::Access::DEVELOPER)
+        end
+
+        it 'does not load' do
+          expect(user.ci_authorized_runners).to be_empty
+        end
+      end
+    end
+
+    context 'with groups projects runners' do
+      let(:group) { create(:group) }
+      let(:project) { create(:project, group: group) }
+
+      def add_user(access)
+        group.add_user(user, access)
+      end
+
+      it_behaves_like :member
+    end
+
+    context 'with other projects runners' do
+      let(:project) { create(:project) }
+
+      def add_user(access)
+        project.team << [user, access]
+      end
+
+      it_behaves_like :member
+    end
+  end
+
   describe '#viewable_starred_projects' do
     let(:user) { create(:user) }
     let(:public_project) { create(:empty_project, :public) }
diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb
index 0fbc984c06144b79023e354bb1a4f26da883659f..ac85f3409225d882399a6571ad2aaa80423814b8 100644
--- a/spec/requests/api/builds_spec.rb
+++ b/spec/requests/api/builds_spec.rb
@@ -9,8 +9,8 @@ describe API::API, api: true  do
   let!(:project) { create(:project, creator_id: user.id) }
   let!(:developer) { create(:project_member, :developer, user: user, project: project) }
   let!(:reporter) { create(:project_member, :reporter, user: user2, project: project) }
-  let(:commit) { create(:ci_commit, project: project)}
-  let(:build) { create(:ci_build, commit: commit) }
+  let(:pipeline) { create(:ci_pipeline, project: project)}
+  let(:build) { create(:ci_build, pipeline: pipeline) }
 
   describe 'GET /projects/:id/builds ' do
     let(:query) { '' }
@@ -59,8 +59,8 @@ describe API::API, api: true  do
 
   describe 'GET /projects/:id/repository/commits/:sha/builds' do
     before do
-      project.ensure_ci_commit(commit.sha, 'master')
-      get api("/projects/#{project.id}/repository/commits/#{commit.sha}/builds", api_user)
+      project.ensure_pipeline(pipeline.sha, 'master')
+      get api("/projects/#{project.id}/repository/commits/#{pipeline.sha}/builds", api_user)
     end
 
     context 'authorized user' do
@@ -102,7 +102,7 @@ describe API::API, api: true  do
     before { get api("/projects/#{project.id}/builds/#{build.id}/artifacts", api_user) }
 
     context 'build with artifacts' do
-      let(:build) { create(:ci_build, :artifacts, commit: commit) }
+      let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
 
       context 'authorized user' do
         let(:download_headers) do
@@ -131,7 +131,7 @@ describe API::API, api: true  do
   end
 
   describe 'GET /projects/:id/builds/:build_id/trace' do
-    let(:build) { create(:ci_build, :trace, commit: commit) }
+    let(:build) { create(:ci_build, :trace, pipeline: pipeline) }
     
     before { get api("/projects/#{project.id}/builds/#{build.id}/trace", api_user) }
 
@@ -181,7 +181,7 @@ describe API::API, api: true  do
   end
 
   describe 'POST /projects/:id/builds/:build_id/retry' do
-    let(:build) { create(:ci_build, :canceled, commit: commit) }
+    let(:build) { create(:ci_build, :canceled, pipeline: pipeline) }
 
     before { post api("/projects/#{project.id}/builds/#{build.id}/retry", api_user) }
 
@@ -218,7 +218,7 @@ describe API::API, api: true  do
     end
 
     context 'build is erasable' do
-      let(:build) { create(:ci_build, :trace, :artifacts, :success, project: project, commit: commit) }
+      let(:build) { create(:ci_build, :trace, :artifacts, :success, project: project, pipeline: pipeline) }
 
       it 'should erase build content' do
         expect(response.status).to eq 201
@@ -234,11 +234,37 @@ describe API::API, api: true  do
     end
 
     context 'build is not erasable' do
-      let(:build) { create(:ci_build, :trace, project: project, commit: commit) }
+      let(:build) { create(:ci_build, :trace, project: project, pipeline: pipeline) }
 
       it 'should respond with forbidden' do
         expect(response.status).to eq 403
       end
     end
   end
+
+  describe 'POST /projects/:id/builds/:build_id/artifacts/keep' do
+    before do
+      post api("/projects/#{project.id}/builds/#{build.id}/artifacts/keep", user)
+    end
+
+    context 'artifacts did not expire' do
+      let(:build) do
+        create(:ci_build, :trace, :artifacts, :success,
+               project: project, pipeline: pipeline, artifacts_expire_at: Time.now + 7.days)
+      end
+
+      it 'keeps artifacts' do
+        expect(response.status).to eq 200
+        expect(build.reload.artifacts_expire_at).to be_nil
+      end
+    end
+
+    context 'no artifacts' do
+      let(:build) { create(:ci_build, project: project, pipeline: pipeline) }
+
+      it 'responds with not found' do
+        expect(response.status).to eq 404
+      end
+    end
+  end
 end
diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb
index 633927c8c3e7aa4f551c2d25bc30241318d5e572..298cdbad329dd3811045167d01008c5979c8dd37 100644
--- a/spec/requests/api/commit_statuses_spec.rb
+++ b/spec/requests/api/commit_statuses_spec.rb
@@ -5,7 +5,7 @@ describe API::CommitStatuses, api: true do
 
   let!(:project) { create(:project) }
   let(:commit) { project.repository.commit }
-  let(:commit_status) { create(:commit_status, commit: ci_commit) }
+  let(:commit_status) { create(:commit_status, pipeline: pipeline) }
   let(:guest) { create_user(:guest) }
   let(:reporter) { create_user(:reporter) }
   let(:developer) { create_user(:developer) }
@@ -16,8 +16,8 @@ describe API::CommitStatuses, api: true do
     let(:get_url) { "/projects/#{project.id}/repository/commits/#{sha}/statuses" }
 
     context 'ci commit exists' do
-      let!(:master) { project.ci_commits.create(sha: commit.id, ref: 'master') }
-      let!(:develop) { project.ci_commits.create(sha: commit.id, ref: 'develop') }
+      let!(:master) { project.pipelines.create(sha: commit.id, ref: 'master') }
+      let!(:develop) { project.pipelines.create(sha: commit.id, ref: 'develop') }
 
       it_behaves_like 'a paginated resources' do
         let(:request) { get api(get_url, reporter) }
@@ -27,7 +27,7 @@ describe API::CommitStatuses, api: true do
         let(:statuses_id) { json_response.map { |status| status['id'] } }
 
         def create_status(commit, opts = {})
-          create(:commit_status, { commit: commit, ref: commit.ref }.merge(opts))
+          create(:commit_status, { pipeline: commit, ref: commit.ref }.merge(opts))
         end
 
         let!(:status1) { create_status(master, status: 'running') }
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index cb82ca7802daf94d263e724726247404fe13f48e..6fc38f537d3df10afb28cd58550b8337f2b9c174 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -90,10 +90,10 @@ describe API::API, api: true  do
       end
 
       it "should return status for CI" do
-        ci_commit = project.ensure_ci_commit(project.repository.commit.sha, 'master')
+        pipeline = project.ensure_pipeline(project.repository.commit.sha, 'master')
         get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
         expect(response.status).to eq(200)
-        expect(json_response['status']).to eq(ci_commit.status)
+        expect(json_response['status']).to eq(pipeline.status)
       end
     end
 
diff --git a/spec/requests/api/gitignores_spec.rb b/spec/requests/api/gitignores_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..aab2d8c81b997ba7b37f0e9911b7158cb7abb0f1
--- /dev/null
+++ b/spec/requests/api/gitignores_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+
+describe API::Gitignores, api: true  do
+  include ApiHelpers
+
+  describe 'Entity Gitignore' do
+    before { get api('/gitignores/Ruby') }
+
+    it { expect(json_response['name']).to eq('Ruby') }
+    it { expect(json_response['content']).to include('*.gem') }
+  end
+
+  describe 'Entity GitignoresList' do
+    before { get api('/gitignores') }
+
+    it { expect(json_response.first['name']).not_to be_nil }
+    it { expect(json_response.first['content']).to be_nil }
+  end
+
+  describe 'GET /gitignores' do
+    it 'returns a list of available license templates' do
+      get api('/gitignores')
+
+      expect(response.status).to eq(200)
+      expect(json_response).to be_an Array
+      expect(json_response.size).to be > 15
+    end
+  end
+end
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 1740726aa48827206dc9fd367ad10e60344c600b..a7ab4c5d9962a1fc5f181dcbb9553a4c2959bb3b 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -12,6 +12,7 @@ describe API::API, api: true  do
   let!(:group2) { create(:group, :private) }
   let!(:project1) { create(:project, namespace: group1) }
   let!(:project2) { create(:project, namespace: group2) }
+  let!(:project3) { create(:project, namespace: group1, path: 'test', visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
 
   before do
     group1.add_owner(user1)
@@ -157,9 +158,11 @@ describe API::API, api: true  do
     context "when authenticated as user" do
       it "should return the group's projects" do
         get api("/groups/#{group1.id}/projects", user1)
+
         expect(response.status).to eq(200)
-        expect(json_response.length).to eq(1)
-        expect(json_response.first['name']).to eq(project1.name)
+        expect(json_response.length).to eq(2)
+        project_names = json_response.map { |proj| proj['name' ] }
+        expect(project_names).to match_array([project1.name, project3.name])
       end
 
       it "should not return a non existing group" do
@@ -172,6 +175,16 @@ describe API::API, api: true  do
 
         expect(response.status).to eq(404)
       end
+
+      it "should only return projects to which user has access" do
+        project3.team << [user3, :developer]
+
+        get api("/groups/#{group1.id}/projects", user3)
+
+        expect(response.status).to eq(200)
+        expect(json_response.length).to eq(1)
+        expect(json_response.first['name']).to eq(project3.name)
+      end
     end
 
     context "when authenticated as admin" do
@@ -191,8 +204,10 @@ describe API::API, api: true  do
     context 'when using group path in URL' do
       it 'should return any existing group' do
         get api("/groups/#{group1.path}/projects", admin)
+
         expect(response.status).to eq(200)
-        expect(json_response.first['name']).to eq(project1.name)
+        project_names = json_response.map { |proj| proj['name' ] }
+        expect(project_names).to match_array([project1.name, project3.name])
       end
 
       it 'should not return a non existing group' do
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 37ab9cc8cfef7c9baa2541171cbd289360a7761f..59e557c5b2a21d3c25359462fc947c7408bffc6e 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -5,6 +5,7 @@ describe API::API, api: true  do
   let(:user)        { create(:user) }
   let(:user2)       { create(:user) }
   let(:non_member)  { create(:user) }
+  let(:guest)       { create(:user) }
   let(:author)      { create(:author) }
   let(:assignee)    { create(:assignee) }
   let(:admin)       { create(:user, :admin) }
@@ -41,7 +42,10 @@ describe API::API, api: true  do
   end
   let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) }
 
-  before { project.team << [user, :reporter] }
+  before do
+    project.team << [user, :reporter]
+    project.team << [guest, :guest]
+  end
 
   describe "GET /issues" do
     context "when unauthenticated" do
@@ -144,6 +148,14 @@ describe API::API, api: true  do
       expect(json_response.first['title']).to eq(issue.title)
     end
 
+    it 'should return project issues without confidential issues for project members with guest role' do
+      get api("#{base_url}/issues", guest)
+      expect(response.status).to eq(200)
+      expect(json_response).to be_an Array
+      expect(json_response.length).to eq(2)
+      expect(json_response.first['title']).to eq(issue.title)
+    end
+
     it 'should return project confidential issues for author' do
       get api("#{base_url}/issues", author)
       expect(response.status).to eq(200)
@@ -249,7 +261,6 @@ describe API::API, api: true  do
       expect(json_response['milestone']).to be_a Hash
       expect(json_response['assignee']).to be_a Hash
       expect(json_response['author']).to be_a Hash
-      expect(json_response['user_notes_count']).to be(1)
     end
 
     it "should return a project issue by id" do
@@ -279,6 +290,11 @@ describe API::API, api: true  do
         expect(response.status).to eq(404)
       end
 
+      it "should return 404 for project members with guest role" do
+        get api("/projects/#{project.id}/issues/#{confidential_issue.id}", guest)
+        expect(response.status).to eq(404)
+      end
+
       it "should return confidential issue for project members" do
         get api("/projects/#{project.id}/issues/#{confidential_issue.id}", user)
         expect(response.status).to eq(200)
@@ -414,6 +430,12 @@ describe API::API, api: true  do
         expect(response.status).to eq(403)
       end
 
+      it "should return 403 for project members with guest role" do
+        put api("/projects/#{project.id}/issues/#{confidential_issue.id}", guest),
+          title: 'updated title'
+        expect(response.status).to eq(403)
+      end
+
       it "should update a confidential issue for project members" do
         put api("/projects/#{project.id}/issues/#{confidential_issue.id}", user),
           title: 'updated title'
diff --git a/spec/requests/api/ldap_group_links_spec.rb b/spec/requests/api/ldap_group_links_spec.rb
index 0bc710515b88a1fa2e94187185451dc4f1ae0da9..a2790a69912bbc531193da5e058ba3966a58bc3d 100644
--- a/spec/requests/api/ldap_group_links_spec.rb
+++ b/spec/requests/api/ldap_group_links_spec.rb
@@ -51,7 +51,7 @@ describe API::API, api: true  do
         expect(json_response['provider']).to eq('ldap3')
       end
 
-      #TODO: Correct and activate this test once issue #329 is fixed
+      # TODO: Correct and activate this test once issue #329 is fixed
       xit "should return ok and add ldap group link even if no provider specified" do
         expect do
           post api("/groups/#{group_with_ldap_links.id}/ldap_group_links", owner),
diff --git a/spec/requests/api/license_spec.rb b/spec/requests/api/license_spec.rb
index a6b3529e59e27d41ae9271b083ac88cc52323eb7..80c7a53f49a2c56c0f1ec0a7f8d041b19a4f6ac3 100644
--- a/spec/requests/api/license_spec.rb
+++ b/spec/requests/api/license_spec.rb
@@ -16,7 +16,7 @@ describe API::API, api: true  do
       expect(Date.parse(json_response['starts_at'])).to eq Date.today - 1.month
       expect(Date.parse(json_response['expires_at'])).to eq Date.today + 11.months
       expect(json_response['active_users']).to eq 1
-      expect(json_response['licensee']).to_not be_empty
+      expect(json_response['licensee']).not_to be_empty
     end
 
     it 'should deny access if not admin' do
@@ -34,7 +34,7 @@ describe API::API, api: true  do
       expect(Date.parse(json_response['starts_at'])).to eq Date.today - 1.month
       expect(Date.parse(json_response['expires_at'])).to eq Date.today + 11.months
       expect(json_response['active_users']).to eq 1
-      expect(json_response['licensee']).to_not be_empty
+      expect(json_response['licensee']).not_to be_empty
     end
 
     it 'denies access if not admin' do
diff --git a/spec/requests/api/license_templates_spec.rb b/spec/requests/api/license_templates_spec.rb
index 64c800f257853a0f6103c614e742c96fcdf393d1..0870d8eefc3c95a9f322cb1e9fc24ab45ce6ecf3 100644
--- a/spec/requests/api/license_templates_spec.rb
+++ b/spec/requests/api/license_templates_spec.rb
@@ -57,7 +57,7 @@ describe API::API, api: true  do
         end
 
         it 'replaces placeholder values' do
-          expect(json_response['content']).to include('Copyright (c) 2016 Anton')
+          expect(json_response['content']).to include("Copyright (c) #{Time.now.year} Anton")
         end
       end
 
@@ -70,7 +70,7 @@ describe API::API, api: true  do
 
         it 'replaces placeholder values' do
           expect(json_response['content']).to include('My Awesome Project')
-          expect(json_response['content']).to include('Copyright (C) 2016  Anton')
+          expect(json_response['content']).to include("Copyright (C) #{Time.now.year}  Anton")
         end
       end
 
@@ -83,7 +83,7 @@ describe API::API, api: true  do
 
         it 'replaces placeholder values' do
           expect(json_response['content']).to include('My Awesome Project')
-          expect(json_response['content']).to include('Copyright (C) 2016  Anton')
+          expect(json_response['content']).to include("Copyright (C) #{Time.now.year}  Anton")
         end
       end
 
@@ -96,7 +96,7 @@ describe API::API, api: true  do
 
         it 'replaces placeholder values' do
           expect(json_response['content']).to include('My Awesome Project')
-          expect(json_response['content']).to include('Copyright (C) 2016  Anton')
+          expect(json_response['content']).to include("Copyright (C) #{Time.now.year}  Anton")
         end
       end
 
@@ -108,7 +108,7 @@ describe API::API, api: true  do
         end
 
         it 'replaces placeholder values' do
-          expect(json_response['content']).to include('Copyright 2016 Anton')
+          expect(json_response['content']).to include("Copyright #{Time.now.year} Anton")
         end
       end
 
@@ -128,7 +128,7 @@ describe API::API, api: true  do
         it 'replaces the copyright owner placeholder with the name of the current user' do
           get api('/licenses/mit', user)
 
-          expect(json_response['content']).to include("Copyright (c) 2016 #{user.name}")
+          expect(json_response['content']).to include("Copyright (c) #{Time.now.year} #{user.name}")
         end
       end
     end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 4b0111df149755c70314c95e9245bfb8f8569294..c59b35a9e58c770a07c28b52bc3a197f0c0e269c 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -138,7 +138,6 @@ describe API::API, api: true  do
       expect(json_response['work_in_progress']).to be_falsy
       expect(json_response['merge_when_build_succeeds']).to be_falsy
       expect(json_response['merge_status']).to eq('can_be_merged')
-      expect(json_response['user_notes_count']).to be(2)
     end
 
     it "should return merge_request" do
@@ -388,7 +387,7 @@ describe API::API, api: true  do
   end
 
   describe "PUT /projects/:id/merge_requests/:merge_request_id/merge" do
-    let(:ci_commit) { create(:ci_commit_without_jobs) }
+    let(:pipeline) { create(:ci_pipeline_without_jobs) }
 
     it "should return merge_request in case of success" do
       put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
@@ -420,6 +419,15 @@ describe API::API, api: true  do
       expect(json_response['message']).to eq('405 Method Not Allowed')
     end
 
+    it 'returns 405 if the build failed for a merge request that requires success' do
+      allow_any_instance_of(MergeRequest).to receive(:mergeable_ci_state?).and_return(false)
+
+      put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
+
+      expect(response.status).to eq(405)
+      expect(json_response['message']).to eq('405 Method Not Allowed')
+    end
+
     it "should return 401 if user has no permissions to merge" do
       user2 = create(:user)
       project.team << [user2, :reporter]
@@ -428,9 +436,22 @@ describe API::API, api: true  do
       expect(json_response['message']).to eq('401 Unauthorized')
     end
 
+    it "returns 409 if the SHA parameter doesn't match" do
+      put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), sha: merge_request.source_sha.succ
+
+      expect(response.status).to eq(409)
+      expect(json_response['message']).to start_with('SHA does not match HEAD of source branch')
+    end
+
+    it "succeeds if the SHA parameter matches" do
+      put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), sha: merge_request.source_sha
+
+      expect(response.status).to eq(200)
+    end
+
     it "enables merge when build succeeds if the ci is active" do
-      allow_any_instance_of(MergeRequest).to receive(:ci_commit).and_return(ci_commit)
-      allow(ci_commit).to receive(:active?).and_return(true)
+      allow_any_instance_of(MergeRequest).to receive(:pipeline).and_return(pipeline)
+      allow(pipeline).to receive(:active?).and_return(true)
 
       put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), merge_when_build_succeeds: true
 
@@ -542,6 +563,21 @@ describe API::API, api: true  do
       expect(json_response).to be_an Array
       expect(json_response.length).to eq(0)
     end
+
+    it 'handles external issues' do
+      jira_project = create(:jira_project, :public, name: 'JIR_EXT1')
+      issue = ExternalIssue.new("#{jira_project.name}-123", jira_project)
+      merge_request = create(:merge_request, :simple, author: user, assignee: user, source_project: jira_project)
+      merge_request.update_attribute(:description, "Closes #{issue.to_reference(jira_project)}")
+
+      get api("/projects/#{jira_project.id}/merge_requests/#{merge_request.id}/closes_issues", user)
+
+      expect(response.status).to eq(200)
+      expect(json_response).to be_an Array
+      expect(json_response.length).to eq(1)
+      expect(json_response.first['title']).to eq(issue.title)
+      expect(json_response.first['id']).to eq(issue.id)
+    end
   end
 
   describe 'POST :id/merge_requests/:merge_request_id/subscription' do
@@ -586,6 +622,39 @@ describe API::API, api: true  do
     end
   end
 
+  describe 'GET :id/merge_requests/:merge_request_id/approvals' do
+    it 'retrieves the approval status' do
+      project.update_attribute(:approvals_before_merge, 2)
+
+      get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/approvals", user)
+
+      expect(response.status).to eq(200)
+      expect(json_response['approvals_required']).to eq 2
+    end
+  end
+
+  describe 'POST :id/merge_requests/:merge_request_id/approve' do
+    context 'when the user is an allowed approver' do
+      it 'approves the merge request' do
+        project.update_attribute(:approvals_before_merge, 2)
+
+        post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/approve", admin)
+
+        expect(response.status).to eq(201)
+        expect(json_response['approvals_left']).to eq(1)
+        expect(json_response['approved_by'][0]['user']['username']).to eq(admin.username)
+      end
+    end
+
+    context 'when the user is the MR author' do
+      it 'returns a not authorised response' do
+        post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/approve", user)
+
+        expect(response.status).to eq(401)
+      end
+    end
+  end
+
   def mr_with_later_created_and_updated_at_time
     merge_request
     merge_request.created_at += 1.hour
diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb
index 241995041bb113828eac54c99efb9dd4e512f578..0154d1c62cc02f4bd9661d4907e1c3435bc13234 100644
--- a/spec/requests/api/milestones_spec.rb
+++ b/spec/requests/api/milestones_spec.rb
@@ -146,6 +146,7 @@ describe API::API, api: true  do
       let(:milestone) { create(:milestone, project: public_project) }
       let(:issue) { create(:issue, project: public_project) }
       let(:confidential_issue) { create(:issue, confidential: true, project: public_project) }
+
       before do
         public_project.team << [user, :developer]
         milestone.issues << issue << confidential_issue
@@ -160,6 +161,18 @@ describe API::API, api: true  do
         expect(json_response.map { |issue| issue['id'] }).to include(issue.id, confidential_issue.id)
       end
 
+      it 'does not return confidential issues to team members with guest role' do
+        member = create(:user)
+        project.team << [member, :guest]
+
+        get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", member)
+
+        expect(response.status).to eq(200)
+        expect(json_response).to be_an Array
+        expect(json_response.size).to eq(1)
+        expect(json_response.map { |issue| issue['id'] }).to include(issue.id)
+      end
+
       it 'does not return confidential issues to regular users' do
         get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", create(:user))
 
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index ed1ed5aeb954a18a92030e4c9f5e404488d4f0c4..beb29a686920987541bb3b3f24c0b0d49934ba71 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -258,8 +258,8 @@ describe API::API, api: true  do
              body: 'Hi!'
       end
 
-      it 'responds with 500' do
-        expect(response.status).to eq 500
+      it 'responds with resource not found error' do
+        expect(response.status).to eq 404
       end
 
       it 'does not create new note' do
diff --git a/spec/requests/api/project_members_spec.rb b/spec/requests/api/project_members_spec.rb
index 3004c2ad23be2d0938dc3d86975e5e700d7eb3a6..ca6ac0300fba795a93798083bd9990229a25578e 100644
--- a/spec/requests/api/project_members_spec.rb
+++ b/spec/requests/api/project_members_spec.rb
@@ -145,7 +145,7 @@ describe API::API, api: true  do
       delete api("/projects/#{project.id}/members/#{user3.id}", user)
       expect do
         delete api("/projects/#{project.id}/members/#{user3.id}", user)
-      end.to_not change { ProjectMember.count }
+      end.not_to change { ProjectMember.count }
       expect(response.status).to eq(200)
     end
 
diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb
index 0510b77a39b292ddabfe3df1aea69094b982fb3b..fdd4ec6d7614ca5c0f728f6db828cc4eceed6fe4 100644
--- a/spec/requests/api/triggers_spec.rb
+++ b/spec/requests/api/triggers_spec.rb
@@ -23,7 +23,7 @@ describe API::API do
     end
 
     before do
-      stub_ci_commit_to_return_yaml_file
+      stub_ci_pipeline_to_return_yaml_file
     end
 
     context 'Handles errors' do
@@ -44,13 +44,13 @@ describe API::API do
     end
 
     context 'Have a commit' do
-      let(:commit) { project.ci_commits.last }
+      let(:pipeline) { project.pipelines.last }
 
       it 'should create builds' do
         post api("/projects/#{project.id}/trigger/builds"), options.merge(ref: 'master')
         expect(response.status).to eq(201)
-        commit.builds.reload
-        expect(commit.builds.size).to eq(2)
+        pipeline.builds.reload
+        expect(pipeline.builds.size).to eq(2)
       end
 
       it 'should return bad request with no builds created if there\'s no commit for that ref' do
@@ -79,8 +79,8 @@ describe API::API do
         it 'create trigger request with variables' do
           post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: variables, ref: 'master')
           expect(response.status).to eq(201)
-          commit.builds.reload
-          expect(commit.builds.first.trigger_request.variables).to eq(variables)
+          pipeline.builds.reload
+          expect(pipeline.builds.first.trigger_request.variables).to eq(variables)
         end
       end
     end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 9b91f90ea9fae08d910025ac57b7dbfc57f3a41e..503b547dc87fa3abd9d840b87c803991556da8ee 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -20,7 +20,7 @@ describe API::API, api: true  do
     end
 
     context "when authenticated" do
-      #These specs are written just in case API authentication is not required anymore
+      # These specs are written just in case API authentication is not required anymore
       context "when public level is restricted" do
         before do
           stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index 7ebf8e41f3bb1f44a76201ca96448a94ab3104b4..7e50bea90d1e12fcdf55e4a932743881fd940544 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -7,7 +7,7 @@ describe Ci::API::API do
   let(:project) { FactoryGirl.create(:empty_project) }
 
   before do
-    stub_ci_commit_to_return_yaml_file
+    stub_ci_pipeline_to_return_yaml_file
   end
 
   describe "Builds API for runners" do
@@ -20,9 +20,9 @@ describe Ci::API::API do
 
     describe "POST /builds/register" do
       it "should start a build" do
-        commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master')
-        commit.create_builds(nil)
-        build = commit.builds.first
+        pipeline = FactoryGirl.create(:ci_pipeline, project: project, ref: 'master')
+        pipeline.create_builds(nil)
+        build = pipeline.builds.first
 
         post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
 
@@ -38,8 +38,8 @@ describe Ci::API::API do
       end
 
       it "should return 404 error if no builds for specific runner" do
-        commit = FactoryGirl.create(:ci_commit, project: shared_project)
-        FactoryGirl.create(:ci_build, commit: commit, status: 'pending')
+        pipeline = FactoryGirl.create(:ci_pipeline, project: shared_project)
+        FactoryGirl.create(:ci_build, pipeline: pipeline, status: 'pending')
 
         post ci_api("/builds/register"), token: runner.token
 
@@ -47,8 +47,8 @@ describe Ci::API::API do
       end
 
       it "should return 404 error if no builds for shared runner" do
-        commit = FactoryGirl.create(:ci_commit, project: project)
-        FactoryGirl.create(:ci_build, commit: commit, status: 'pending')
+        pipeline = FactoryGirl.create(:ci_pipeline, project: project)
+        FactoryGirl.create(:ci_build, pipeline: pipeline, status: 'pending')
 
         post ci_api("/builds/register"), token: shared_runner.token
 
@@ -56,8 +56,8 @@ describe Ci::API::API do
       end
 
       it "returns options" do
-        commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master')
-        commit.create_builds(nil)
+        pipeline = FactoryGirl.create(:ci_pipeline, project: project, ref: 'master')
+        pipeline.create_builds(nil)
 
         post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
 
@@ -66,8 +66,8 @@ describe Ci::API::API do
       end
 
       it "returns variables" do
-        commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master')
-        commit.create_builds(nil)
+        pipeline = FactoryGirl.create(:ci_pipeline, project: project, ref: 'master')
+        pipeline.create_builds(nil)
         project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value")
 
         post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
@@ -83,10 +83,10 @@ describe Ci::API::API do
 
       it "returns variables for triggers" do
         trigger = FactoryGirl.create(:ci_trigger, project: project)
-        commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master')
+        pipeline = FactoryGirl.create(:ci_pipeline, project: project, ref: 'master')
 
-        trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, commit: commit, trigger: trigger)
-        commit.create_builds(nil, trigger_request)
+        trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, pipeline: pipeline, trigger: trigger)
+        pipeline.create_builds(nil, trigger_request)
         project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value")
 
         post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
@@ -103,9 +103,9 @@ describe Ci::API::API do
       end
 
       it "returns dependent builds" do
-        commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master')
-        commit.create_builds(nil, nil)
-        commit.builds.where(stage: 'test').each(&:success)
+        pipeline = FactoryGirl.create(:ci_pipeline, project: project, ref: 'master')
+        pipeline.create_builds(nil, nil)
+        pipeline.builds.where(stage: 'test').each(&:success)
 
         post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
 
@@ -131,8 +131,8 @@ describe Ci::API::API do
 
       context 'when build has no tags' do
         before do
-          commit = create(:ci_commit, project: project)
-          create(:ci_build, commit: commit, tags: [])
+          pipeline = create(:ci_pipeline, project: project)
+          create(:ci_build, pipeline: pipeline, tags: [])
         end
 
         context 'when runner is allowed to pick untagged builds' do
@@ -163,8 +163,8 @@ describe Ci::API::API do
     end
 
     describe "PUT /builds/:id" do
-      let(:commit) {create(:ci_commit, project: project)}
-      let(:build) { create(:ci_build, :trace, commit: commit, runner_id: runner.id) }
+      let(:pipeline) {create(:ci_pipeline, project: project)}
+      let(:build) { create(:ci_build, :trace, pipeline: pipeline, runner_id: runner.id) }
 
       before do
         build.run!
@@ -237,8 +237,8 @@ describe Ci::API::API do
     context "Artifacts" do
       let(:file_upload) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
       let(:file_upload2) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/gif') }
-      let(:commit) { create(:ci_commit, project: project) }
-      let(:build) { create(:ci_build, commit: commit, runner_id: runner.id) }
+      let(:pipeline) { create(:ci_pipeline, project: project) }
+      let(:build) { create(:ci_build, pipeline: pipeline, runner_id: runner.id) }
       let(:authorize_url) { ci_api("/builds/#{build.id}/artifacts/authorize") }
       let(:post_url) { ci_api("/builds/#{build.id}/artifacts") }
       let(:delete_url) { ci_api("/builds/#{build.id}/artifacts") }
@@ -253,13 +253,13 @@ describe Ci::API::API do
           it "using token as parameter" do
             post authorize_url, { token: build.token }, headers
             expect(response.status).to eq(200)
-            expect(json_response["TempPath"]).to_not be_nil
+            expect(json_response["TempPath"]).not_to be_nil
           end
 
           it "using token as header" do
             post authorize_url, {}, headers_with_token
             expect(response.status).to eq(200)
-            expect(json_response["TempPath"]).to_not be_nil
+            expect(json_response["TempPath"]).not_to be_nil
           end
         end
 
@@ -364,6 +364,42 @@ describe Ci::API::API do
             end
           end
 
+          context 'with an expire date' do
+            let!(:artifacts) { file_upload }
+
+            let(:post_data) do
+              { 'file.path' => artifacts.path,
+                'file.name' => artifacts.original_filename,
+                'expire_in' => expire_in }
+            end
+
+            before do
+              post(post_url, post_data, headers_with_token)
+            end
+
+            context 'with an expire_in given' do
+              let(:expire_in) { '7 days' }
+
+              it 'updates when specified' do
+                build.reload
+                expect(response.status).to eq(201)
+                expect(json_response['artifacts_expire_at']).not_to be_empty
+                expect(build.artifacts_expire_at).to be_within(5.minutes).of(Time.now + 7.days)
+              end
+            end
+
+            context 'with no expire_in given' do
+              let(:expire_in) { nil }
+
+              it 'ignores if not specified' do
+                build.reload
+                expect(response.status).to eq(201)
+                expect(json_response['artifacts_expire_at']).to be_nil
+                expect(build.artifacts_expire_at).to be_nil
+              end
+            end
+          end
+
           context "artifacts file is too large" do
             it "should fail to post too large artifact" do
               stub_application_setting(max_artifacts_size: 0)
diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb
index 0ef03f9371b4db99927355c5edd886db69d757d9..72f6a3c981daeae00d75d77d5944d4bff9955d57 100644
--- a/spec/requests/ci/api/triggers_spec.rb
+++ b/spec/requests/ci/api/triggers_spec.rb
@@ -15,7 +15,7 @@ describe Ci::API::API do
     end
 
     before do
-      stub_ci_commit_to_return_yaml_file
+      stub_ci_pipeline_to_return_yaml_file
     end
 
     context 'Handles errors' do
@@ -36,13 +36,13 @@ describe Ci::API::API do
     end
 
     context 'Have a commit' do
-      let(:commit) { project.ci_commits.last }
+      let(:pipeline) { project.pipelines.last }
 
       it 'should create builds' do
         post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options
         expect(response.status).to eq(201)
-        commit.builds.reload
-        expect(commit.builds.size).to eq(2)
+        pipeline.builds.reload
+        expect(pipeline.builds.size).to eq(2)
       end
 
       it 'should return bad request with no builds created if there\'s no commit for that ref' do
@@ -71,8 +71,8 @@ describe Ci::API::API do
         it 'create trigger request with variables' do
           post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options.merge(variables: variables)
           expect(response.status).to eq(201)
-          commit.builds.reload
-          expect(commit.builds.first.trigger_request.variables).to eq(variables)
+          pipeline.builds.reload
+          expect(pipeline.builds.first.trigger_request.variables).to eq(variables)
         end
       end
     end
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fd26ca978187200693c509accf2d73dfc0aac30b
--- /dev/null
+++ b/spec/requests/git_http_spec.rb
@@ -0,0 +1,395 @@
+require "spec_helper"
+
+describe 'Git HTTP requests', lib: true do
+  let(:user)    { create(:user) }
+  let(:project) { create(:project, path: 'project.git-project') }
+
+  it "gives WWW-Authenticate hints" do
+    clone_get('doesnt/exist.git')
+
+    expect(response.header['WWW-Authenticate']).to start_with('Basic ')
+  end
+
+  context "when the project doesn't exist" do
+    context "when no authentication is provided" do
+      it "responds with status 401 (no project existence information leak)" do
+        download('doesnt/exist.git') do |response|
+          expect(response.status).to eq(401)
+        end
+      end
+    end
+
+    context "when username and password are provided" do
+      context "when authentication fails" do
+        it "responds with status 401" do
+          download('doesnt/exist.git', user: user.username, password: "nope") do |response|
+            expect(response.status).to eq(401)
+          end
+        end
+      end
+
+      context "when authentication succeeds" do
+        it "responds with status 404" do
+          download('/doesnt/exist.git', user: user.username, password: user.password) do |response|
+            expect(response.status).to eq(404)
+          end
+        end
+      end
+    end
+  end
+
+  context "when the Wiki for a project exists" do
+    it "responds with the right project" do
+      wiki = ProjectWiki.new(project)
+      project.update_attribute(:visibility_level, Project::PUBLIC)
+
+      download("/#{wiki.repository.path_with_namespace}.git") do |response|
+        json_body = ActiveSupport::JSON.decode(response.body)
+
+        expect(response.status).to eq(200)
+        expect(json_body['RepoPath']).to include(wiki.repository.path_with_namespace)
+      end
+    end
+  end
+
+  context "when the project exists" do
+    let(:path) { "#{project.path_with_namespace}.git" }
+
+    context "when the project is public" do
+      before do
+        project.update_attribute(:visibility_level, Project::PUBLIC)
+      end
+
+      it "downloads get status 200" do
+        download(path, {}) do |response|
+          expect(response.status).to eq(200)
+        end
+      end
+
+      it "uploads get status 401" do
+        upload(path, {}) do |response|
+          expect(response.status).to eq(401)
+        end
+      end
+
+      context "with correct credentials" do
+        let(:env) { { user: user.username, password: user.password } }
+
+        it "uploads get status 200 (because Git hooks do the real check)" do
+          upload(path, env) do |response|
+            expect(response.status).to eq(200)
+          end
+        end
+
+        context 'but git-receive-pack is disabled' do
+          it "responds with status 404" do
+            allow(Gitlab.config.gitlab_shell).to receive(:receive_pack).and_return(false)
+
+            upload(path, env) do |response|
+              expect(response.status).to eq(404)
+            end
+          end
+        end
+      end
+
+      context 'but git-upload-pack is disabled' do
+        it "responds with status 404" do
+          allow(Gitlab.config.gitlab_shell).to receive(:upload_pack).and_return(false)
+
+          download(path, {}) do |response|
+            expect(response.status).to eq(404)
+          end
+        end
+      end
+    end
+
+    context "when the project is private" do
+      before do
+        project.update_attribute(:visibility_level, Project::PRIVATE)
+      end
+
+      context "when no authentication is provided" do
+        it "responds with status 401 to downloads" do
+          download(path, {}) do |response|
+            expect(response.status).to eq(401)
+          end
+        end
+
+        it "responds with status 401 to uploads" do
+          upload(path, {}) do |response|
+            expect(response.status).to eq(401)
+          end
+        end
+      end
+
+      context "when username and password are provided" do
+        let(:env) { { user: user.username, password: 'nope' } }
+
+        context "when authentication fails" do
+          it "responds with status 401" do
+            download(path, env) do |response|
+              expect(response.status).to eq(401)
+            end
+          end
+
+          context "when the user is IP banned" do
+            it "responds with status 401" do
+              expect(Rack::Attack::Allow2Ban).to receive(:filter).and_return(true)
+              allow_any_instance_of(Rack::Request).to receive(:ip).and_return('1.2.3.4')
+
+              clone_get(path, env)
+
+              expect(response.status).to eq(401)
+            end
+          end
+        end
+
+        context "when authentication succeeds" do
+          let(:env) { { user: user.username, password: user.password } }
+
+          context "when the user has access to the project" do
+            before do
+              project.team << [user, :master]
+            end
+
+            context "when the user is blocked" do
+              it "responds with status 404" do
+                user.block
+                project.team << [user, :master]
+
+                download(path, env) do |response|
+                  expect(response.status).to eq(404)
+                end
+              end
+            end
+
+            context "when the user isn't blocked" do
+              it "downloads get status 200" do
+                expect(Rack::Attack::Allow2Ban).to receive(:reset)
+
+                clone_get(path, env)
+
+                expect(response.status).to eq(200)
+              end
+
+              it "uploads get status 200" do
+                upload(path, env) do |response|
+                  expect(response.status).to eq(200)
+                end
+              end
+            end
+
+            context "when an oauth token is provided" do
+              before do
+                application = Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user)
+                @token = Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id)
+              end
+
+              it "downloads get status 200" do
+                clone_get "#{project.path_with_namespace}.git", user: 'oauth2', password: @token.token
+
+                expect(response.status).to eq(200)
+              end
+
+              it "uploads get status 401 (no project existence information leak)" do
+                push_get "#{project.path_with_namespace}.git", user: 'oauth2', password: @token.token
+
+                expect(response.status).to eq(401)
+              end
+            end
+
+            context "when blank password attempts follow a valid login" do
+              def attempt_login(include_password)
+                password = include_password ? user.password : ""
+                clone_get path, user: user.username, password: password
+                response.status
+              end
+
+              it "repeated attempts followed by successful attempt" do
+                options = Gitlab.config.rack_attack.git_basic_auth
+                maxretry = options[:maxretry] - 1
+                ip = '1.2.3.4'
+
+                allow_any_instance_of(Rack::Request).to receive(:ip).and_return(ip)
+                Rack::Attack::Allow2Ban.reset(ip, options)
+
+                maxretry.times.each do
+                  expect(attempt_login(false)).to eq(401)
+                end
+
+                expect(attempt_login(true)).to eq(200)
+                expect(Rack::Attack::Allow2Ban.banned?(ip)).to be_falsey
+
+                maxretry.times.each do
+                  expect(attempt_login(false)).to eq(401)
+                end
+
+                Rack::Attack::Allow2Ban.reset(ip, options)
+              end
+            end
+          end
+
+          context "when the user doesn't have access to the project" do
+            it "downloads get status 404" do
+              download(path, user: user.username, password: user.password) do |response|
+                expect(response.status).to eq(404)
+              end
+            end
+
+            it "uploads get status 200 (because Git hooks do the real check)" do
+              upload(path, user: user.username, password: user.password) do |response|
+                expect(response.status).to eq(200)
+              end
+            end
+          end
+        end
+      end
+
+      context "when a gitlab ci token is provided" do
+        let(:token) { 123 }
+        let(:project) { FactoryGirl.create :empty_project }
+
+        before do
+          project.update_attributes(runners_token: token, builds_enabled: true)
+        end
+
+        it "downloads get status 200" do
+          clone_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: token
+
+          expect(response.status).to eq(200)
+        end
+
+        it "uploads get status 401 (no project existence information leak)" do
+          push_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: token
+
+          expect(response.status).to eq(401)
+        end
+      end
+    end
+  end
+
+  context "when the project path doesn't end in .git" do
+    context "GET info/refs" do
+      let(:path) { "/#{project.path_with_namespace}/info/refs" }
+
+      context "when no params are added" do
+        before { get path }
+
+        it "redirects to the .git suffix version" do
+          expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs")
+        end
+      end
+
+      context "when the upload-pack service is requested" do
+        let(:params) { { service: 'git-upload-pack' } }
+        before { get path, params }
+
+        it "redirects to the .git suffix version" do
+          expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs?service=#{params[:service]}")
+        end
+      end
+
+      context "when the receive-pack service is requested" do
+        let(:params) { { service: 'git-receive-pack' } }
+        before { get path, params }
+
+        it "redirects to the .git suffix version" do
+          expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs?service=#{params[:service]}")
+        end
+      end
+
+      context "when the params are anything else" do
+        let(:params) { { service: 'git-implode-pack' } }
+        before { get path, params }
+
+        it "redirects to the sign-in page" do
+          expect(response).to redirect_to(new_user_session_path)
+        end
+      end
+    end
+
+    context "POST git-upload-pack" do
+      it "fails to find a route" do
+        expect { clone_post(project.path_with_namespace) }.to raise_error(ActionController::RoutingError)
+      end
+    end
+
+    context "POST git-receive-pack" do
+      it "failes to find a route" do
+        expect { push_post(project.path_with_namespace) }.to raise_error(ActionController::RoutingError)
+      end
+    end
+  end
+
+  context "retrieving an info/refs file" do
+    before { project.update_attribute(:visibility_level, Project::PUBLIC) }
+
+    context "when the file exists" do
+      before do
+        # Provide a dummy file in its place
+        allow_any_instance_of(Repository).to receive(:blob_at).and_call_original
+        allow_any_instance_of(Repository).to receive(:blob_at).with('5937ac0a7beb003549fc5fd26fc247adbce4a52e', 'info/refs') do
+          Gitlab::Git::Blob.find(project.repository, 'master', '.gitignore')
+        end
+
+        get "/#{project.path_with_namespace}/blob/master/info/refs"
+      end
+
+      it "returns the file" do
+        expect(response.status).to eq(200)
+      end
+    end
+
+    context "when the file does not exist" do
+      before { get "/#{project.path_with_namespace}/blob/master/info/refs" }
+
+      it "returns not found" do
+        expect(response.status).to eq(404)
+      end
+    end
+  end
+
+  def clone_get(project, options={})
+    get "/#{project}/info/refs", { service: 'git-upload-pack' }, auth_env(*options.values_at(:user, :password))
+  end
+
+  def clone_post(project, options={})
+    post "/#{project}/git-upload-pack", {}, auth_env(*options.values_at(:user, :password))
+  end
+
+  def push_get(project, options={})
+    get "/#{project}/info/refs", { service: 'git-receive-pack' }, auth_env(*options.values_at(:user, :password))
+  end
+
+  def push_post(project, options={})
+    post "/#{project}/git-receive-pack", {}, auth_env(*options.values_at(:user, :password))
+  end
+
+  def download(project, user: nil, password: nil)
+    args = [project, { user: user, password: password }]
+
+    clone_get(*args)
+    yield response
+
+    clone_post(*args)
+    yield response
+  end
+
+  def upload(project, user: nil, password: nil)
+    args = [project, { user: user, password: password }]
+
+    push_get(*args)
+    yield response
+
+    push_post(*args)
+    yield response
+  end
+
+  def auth_env(user, password)
+    if user && password
+      { 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(user, password) }
+    else
+      {}
+    end
+  end
+end
diff --git a/spec/requests/jwt_controller_spec.rb b/spec/requests/jwt_controller_spec.rb
index d006ff195cf8a9970c2d5b74f90e896950193901..d2d4a9eca18faded89a085ce5c033570b760de7e 100644
--- a/spec/requests/jwt_controller_spec.rb
+++ b/spec/requests/jwt_controller_spec.rb
@@ -44,7 +44,7 @@ describe JwtController do
       let(:user) { create(:user) }
       let(:headers) { { authorization: credentials('user', 'password') } }
 
-      before { expect_any_instance_of(Gitlab::Auth).to receive(:find).with('user', 'password').and_return(user) }
+      before { expect(Gitlab::Auth).to receive(:find_with_user_password).with('user', 'password').and_return(user) }
 
       subject! { get '/jwt/auth', parameters, headers }
 
diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb
index 3f4a1ced2b67c78deaeb02b2e5d403b19a5d045f..c38852956bc7cc5b946cee6a2bfcaf5846635f3a 100644
--- a/spec/services/auth/container_registry_authentication_service_spec.rb
+++ b/spec/services/auth/container_registry_authentication_service_spec.rb
@@ -19,6 +19,26 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
     it { expect(payload).to include('access') }
   end
 
+  shared_examples 'a valid token' do
+    it { is_expected.to include(:token) }
+    it { expect(payload).to include('access') }
+
+    context 'a expirable' do
+      let(:expires_at) { Time.at(payload['exp']) }
+      let(:expire_delay) { 10 }
+
+      context 'for default configuration' do
+        it { expect(expires_at).not_to be_within(2.seconds).of(Time.now + expire_delay.minutes) }
+      end
+
+      context 'for changed configuration' do
+        before { stub_application_setting(container_registry_token_expire_delay: expire_delay) }
+
+        it { expect(expires_at).to be_within(2.seconds).of(Time.now + expire_delay.minutes) }
+      end
+    end
+  end
+
   shared_examples 'a accessible' do
     let(:access) do
       [{
@@ -28,10 +48,15 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
        }]
     end
 
-    it_behaves_like 'an authenticated'
+    it_behaves_like 'a valid token'
     it { expect(payload).to include('access' => access) }
   end
 
+  shared_examples 'an inaccessible' do
+    it_behaves_like 'a valid token'
+    it { expect(payload).to include('access' => []) }
+  end
+
   shared_examples 'a pullable' do
     it_behaves_like 'a accessible' do
       let(:actions) { ['pull'] }
@@ -50,14 +75,20 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
     end
   end
 
-  shared_examples 'an unauthorized' do
-    it { is_expected.to include(http_status: 401) }
-    it { is_expected.to_not include(:token) }
-  end
-
   shared_examples 'a forbidden' do
     it { is_expected.to include(http_status: 403) }
-    it { is_expected.to_not include(:token) }
+    it { is_expected.not_to include(:token) }
+  end
+
+  describe '#full_access_token' do
+    let(:project) { create(:empty_project) }
+    let(:token) { described_class.full_access_token(project.path_with_namespace) }
+
+    subject { { token: token } }
+
+    it_behaves_like 'a accessible' do
+      let(:actions) { ['*'] }
+    end
   end
 
   describe '#full_access_token' do
@@ -75,12 +106,8 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
     let(:project) { create(:project) }
     let(:current_user) { create(:user) }
 
-    context 'allow to use offline_token' do
-      let(:current_params) do
-        { offline_token: true }
-      end
-
-      it_behaves_like 'an authenticated'
+    context 'allow to use scope-less authentication' do
+      it_behaves_like 'a valid token'
     end
 
     context 'allow developer to push images' do
@@ -120,7 +147,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
         { scope: "repository:#{project.path_with_namespace}:pull,push" }
       end
 
-      it_behaves_like 'a forbidden'
+      it_behaves_like 'an inaccessible'
     end
   end
 
@@ -135,6 +162,10 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
       it_behaves_like 'an authenticated'
     end
 
+    context 'allow to use scope-less authentication' do
+      it_behaves_like 'a valid token'
+    end
+
     context 'allow to pull and push images' do
       let(:current_params) do
         { scope: "repository:#{current_project.path_with_namespace}:pull,push" }
@@ -158,7 +189,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
 
         context 'disallow for private' do
           let(:project) { create(:empty_project, :private) }
-          it_behaves_like 'a forbidden'
+          it_behaves_like 'an inaccessible'
         end
       end
 
@@ -169,7 +200,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
 
         context 'disallow for all' do
           let(:project) { create(:empty_project, :public) }
-          it_behaves_like 'a forbidden'
+          it_behaves_like 'an inaccessible'
         end
       end
     end
@@ -184,18 +215,14 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
           { scope: "repository:#{project.path_with_namespace}:pull" }
         end
 
-        it_behaves_like 'a forbidden'
+        it_behaves_like 'an inaccessible'
       end
     end
   end
 
   context 'unauthorized' do
-    context 'disallow to use offline_token' do
-      let(:current_params) do
-        { offline_token: true }
-      end
-
-      it_behaves_like 'an unauthorized'
+    context 'disallow to use scope-less authentication' do
+      it_behaves_like 'a forbidden'
     end
 
     context 'for invalid scope' do
diff --git a/spec/services/ci/create_builds_service_spec.rb b/spec/services/ci/create_builds_service_spec.rb
index ecc3a88a2621ea215f686867fc6b46b0316bc891..984b78487d410d652ea880e9f8b8beddbd2b4fb2 100644
--- a/spec/services/ci/create_builds_service_spec.rb
+++ b/spec/services/ci/create_builds_service_spec.rb
@@ -1,7 +1,7 @@
 require 'spec_helper'
 
 describe Ci::CreateBuildsService, services: true do
-  let(:commit) { create(:ci_commit, ref: 'master') }
+  let(:pipeline) { create(:ci_pipeline, ref: 'master') }
   let(:user) { create(:user) }
 
   describe '#execute' do
@@ -9,7 +9,7 @@ describe Ci::CreateBuildsService, services: true do
     #
 
     subject do
-      described_class.new(commit).execute(commit, nil, user, status)
+      described_class.new(pipeline).execute('test', nil, user, status)
     end
 
     context 'next builds available' do
diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb
index dbdc5370bd8124a61d0b259a2f84ef368ff3f734..ae4b7aca8206bd78b36bb313f2677fb299fb6f43 100644
--- a/spec/services/ci/create_trigger_request_service_spec.rb
+++ b/spec/services/ci/create_trigger_request_service_spec.rb
@@ -6,7 +6,7 @@ describe Ci::CreateTriggerRequestService, services: true do
   let(:trigger) { create(:ci_trigger, project: project) }
 
   before do
-    stub_ci_commit_to_return_yaml_file
+    stub_ci_pipeline_to_return_yaml_file
   end
 
   describe :execute do
@@ -27,8 +27,8 @@ describe Ci::CreateTriggerRequestService, services: true do
       subject { service.execute(project, trigger, 'master') }
 
       before do
-        stub_ci_commit_yaml_file('{}')
-        FactoryGirl.create :ci_commit, project: project
+        stub_ci_pipeline_yaml_file('{}')
+        FactoryGirl.create :ci_pipeline, project: project
       end
 
       it { expect(subject).to be_nil }
diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb
index 4cc4b3870d1943e3dcd8a2c4686315ce07ee505f..476a888e394fb8878f154bc8620b6b6d375767d9 100644
--- a/spec/services/ci/image_for_build_service_spec.rb
+++ b/spec/services/ci/image_for_build_service_spec.rb
@@ -5,8 +5,8 @@ module Ci
     let(:service) { ImageForBuildService.new }
     let(:project) { FactoryGirl.create(:empty_project) }
     let(:commit_sha) { '01234567890123456789' }
-    let(:commit) { project.ensure_ci_commit(commit_sha, 'master') }
-    let(:build) { FactoryGirl.create(:ci_build, commit: commit) }
+    let(:commit) { project.ensure_pipeline(commit_sha, 'master') }
+    let(:build) { FactoryGirl.create(:ci_build, pipeline: commit) }
 
     describe :execute do
       before { build }
diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb
index e81f9e757ac713a6fa69159e9756232ff8375ac5..f28f2f1438ddf2e8cba207e0464cbe8d2ce4044b 100644
--- a/spec/services/ci/register_build_service_spec.rb
+++ b/spec/services/ci/register_build_service_spec.rb
@@ -4,8 +4,8 @@ module Ci
   describe RegisterBuildService, services: true do
     let!(:service) { RegisterBuildService.new }
     let!(:project) { FactoryGirl.create :empty_project, shared_runners_enabled: false }
-    let!(:commit) { FactoryGirl.create :ci_commit, project: project }
-    let!(:pending_build) { FactoryGirl.create :ci_build, commit: commit }
+    let!(:pipeline) { FactoryGirl.create :ci_pipeline, project: project }
+    let!(:pending_build) { FactoryGirl.create :ci_build, pipeline: pipeline }
     let!(:shared_runner) { FactoryGirl.create(:ci_runner, is_shared: true) }
     let!(:specific_runner) { FactoryGirl.create(:ci_runner, is_shared: false) }
 
@@ -45,11 +45,73 @@ module Ci
         end
       end
 
+      context 'deleted projects' do
+        before do
+          project.update(pending_delete: true)
+        end
+
+        context 'for shared runners' do
+          before do
+            project.update(shared_runners_enabled: true)
+          end
+
+          it 'does not pick a build' do
+            expect(service.execute(shared_runner)).to be_nil
+          end
+        end
+
+        context 'for specific runner' do
+          it 'does not pick a build' do
+            expect(service.execute(specific_runner)).to be_nil
+          end
+        end
+      end
+
       context 'allow shared runners' do
         before do
           project.update(shared_runners_enabled: true)
         end
 
+        context 'for multiple builds' do
+          let!(:project2) { create :empty_project, shared_runners_enabled: true }
+          let!(:pipeline2) { create :ci_pipeline, project: project2 }
+          let!(:project3) { create :empty_project, shared_runners_enabled: true }
+          let!(:pipeline3) { create :ci_pipeline, project: project3 }
+          let!(:build1_project1) { pending_build }
+          let!(:build2_project1) { FactoryGirl.create :ci_build, pipeline: pipeline }
+          let!(:build3_project1) { FactoryGirl.create :ci_build, pipeline: pipeline }
+          let!(:build1_project2) { FactoryGirl.create :ci_build, pipeline: pipeline2 }
+          let!(:build2_project2) { FactoryGirl.create :ci_build, pipeline: pipeline2 }
+          let!(:build1_project3) { FactoryGirl.create :ci_build, pipeline: pipeline3 }
+
+          it 'prefers projects without builds first' do
+            # it gets for one build from each of the projects
+            expect(service.execute(shared_runner)).to eq(build1_project1)
+            expect(service.execute(shared_runner)).to eq(build1_project2)
+            expect(service.execute(shared_runner)).to eq(build1_project3)
+
+            # then it gets a second build from each of the projects
+            expect(service.execute(shared_runner)).to eq(build2_project1)
+            expect(service.execute(shared_runner)).to eq(build2_project2)
+
+            # in the end the third build
+            expect(service.execute(shared_runner)).to eq(build3_project1)
+          end
+
+          it 'equalises number of running builds' do
+            # after finishing the first build for project 1, get a second build from the same project
+            expect(service.execute(shared_runner)).to eq(build1_project1)
+            build1_project1.success
+            expect(service.execute(shared_runner)).to eq(build2_project1)
+
+            expect(service.execute(shared_runner)).to eq(build1_project2)
+            build1_project2.success
+            expect(service.execute(shared_runner)).to eq(build2_project2)
+            expect(service.execute(shared_runner)).to eq(build1_project3)
+            expect(service.execute(shared_runner)).to eq(build3_project1)
+          end
+        end
+
         context 'shared runner' do
           let(:build) { service.execute(shared_runner) }
 
diff --git a/spec/services/create_commit_builds_service_spec.rb b/spec/services/create_commit_builds_service_spec.rb
index ea5dcfa068a0ffe114ebc1af7d3c5251b53da1ad..a5b4d9f05de8c6b378189c66b4e5ca76506c87dd 100644
--- a/spec/services/create_commit_builds_service_spec.rb
+++ b/spec/services/create_commit_builds_service_spec.rb
@@ -6,12 +6,12 @@ describe CreateCommitBuildsService, services: true do
   let(:user) { nil }
 
   before do
-    stub_ci_commit_to_return_yaml_file
+    stub_ci_pipeline_to_return_yaml_file
   end
 
   describe :execute do
     context 'valid params' do
-      let(:commit) do
+      let(:pipeline) do
         service.execute(project, user,
                         ref: 'refs/heads/master',
                         before: '00000000',
@@ -20,11 +20,11 @@ describe CreateCommitBuildsService, services: true do
                        )
       end
 
-      it { expect(commit).to be_kind_of(Ci::Commit) }
-      it { expect(commit).to be_valid }
-      it { expect(commit).to be_persisted }
-      it { expect(commit).to eq(project.ci_commits.last) }
-      it { expect(commit.builds.first).to be_kind_of(Ci::Build) }
+      it { expect(pipeline).to be_kind_of(Ci::Pipeline) }
+      it { expect(pipeline).to be_valid }
+      it { expect(pipeline).to be_persisted }
+      it { expect(pipeline).to eq(project.pipelines.last) }
+      it { expect(pipeline.builds.first).to be_kind_of(Ci::Build) }
     end
 
     context "skip tag if there is no build for it" do
@@ -40,7 +40,7 @@ describe CreateCommitBuildsService, services: true do
 
       it "creates commit if there is no appropriate job but deploy job has right ref setting" do
         config = YAML.dump({ deploy: { deploy: "ls", only: ["0_1"] } })
-        stub_ci_commit_yaml_file(config)
+        stub_ci_pipeline_yaml_file(config)
 
         result = service.execute(project, user,
                                  ref: 'refs/heads/0_1',
@@ -52,8 +52,8 @@ describe CreateCommitBuildsService, services: true do
       end
     end
 
-    it 'skips creating ci_commit for refs without .gitlab-ci.yml' do
-      stub_ci_commit_yaml_file(nil)
+    it 'skips creating pipeline for refs without .gitlab-ci.yml' do
+      stub_ci_pipeline_yaml_file(nil)
       result = service.execute(project, user,
                                ref: 'refs/heads/0_1',
                                before: '00000000',
@@ -61,115 +61,115 @@ describe CreateCommitBuildsService, services: true do
                                commits: [{ message: 'Message' }]
                               )
       expect(result).to be_falsey
-      expect(Ci::Commit.count).to eq(0)
+      expect(Ci::Pipeline.count).to eq(0)
     end
 
     it 'fails commits if yaml is invalid' do
       message = 'message'
-      allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message }
-      stub_ci_commit_yaml_file('invalid: file: file')
+      allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { message }
+      stub_ci_pipeline_yaml_file('invalid: file: file')
       commits = [{ message: message }]
-      commit = service.execute(project, user,
-                               ref: 'refs/tags/0_1',
-                               before: '00000000',
-                               after: '31das312',
-                               commits: commits
-                              )
-      expect(commit).to be_persisted
-      expect(commit.builds.any?).to be false
-      expect(commit.status).to eq('failed')
-      expect(commit.yaml_errors).to_not be_nil
+      pipeline = service.execute(project, user,
+                                 ref: 'refs/tags/0_1',
+                                 before: '00000000',
+                                 after: '31das312',
+                                 commits: commits
+                                )
+      expect(pipeline).to be_persisted
+      expect(pipeline.builds.any?).to be false
+      expect(pipeline.status).to eq('failed')
+      expect(pipeline.yaml_errors).not_to be_nil
     end
 
     describe :ci_skip? do
       let(:message) { "some message[ci skip]" }
 
       before do
-        allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message }
+        allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { message }
       end
 
       it "skips builds creation if there is [ci skip] tag in commit message" do
         commits = [{ message: message }]
-        commit = service.execute(project, user,
-                                 ref: 'refs/tags/0_1',
-                                 before: '00000000',
-                                 after: '31das312',
-                                 commits: commits
-                                )
-        expect(commit).to be_persisted
-        expect(commit.builds.any?).to be false
-        expect(commit.status).to eq("skipped")
+        pipeline = service.execute(project, user,
+                                   ref: 'refs/tags/0_1',
+                                   before: '00000000',
+                                   after: '31das312',
+                                   commits: commits
+                                  )
+        expect(pipeline).to be_persisted
+        expect(pipeline.builds.any?).to be false
+        expect(pipeline.status).to eq("skipped")
       end
 
       it "does not skips builds creation if there is no [ci skip] tag in commit message" do
-        allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { "some message" }
+        allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { "some message" }
 
         commits = [{ message: "some message" }]
-        commit = service.execute(project, user,
-                                 ref: 'refs/tags/0_1',
-                                 before: '00000000',
-                                 after: '31das312',
-                                 commits: commits
-                                )
-
-        expect(commit).to be_persisted
-        expect(commit.builds.first.name).to eq("staging")
+        pipeline = service.execute(project, user,
+                                   ref: 'refs/tags/0_1',
+                                   before: '00000000',
+                                   after: '31das312',
+                                   commits: commits
+                                  )
+
+        expect(pipeline).to be_persisted
+        expect(pipeline.builds.first.name).to eq("staging")
       end
 
       it "skips builds creation if there is [ci skip] tag in commit message and yaml is invalid" do
-        stub_ci_commit_yaml_file('invalid: file: fiile')
+        stub_ci_pipeline_yaml_file('invalid: file: fiile')
         commits = [{ message: message }]
-        commit = service.execute(project, user,
-                                 ref: 'refs/tags/0_1',
-                                 before: '00000000',
-                                 after: '31das312',
-                                 commits: commits
-                                )
-        expect(commit).to be_persisted
-        expect(commit.builds.any?).to be false
-        expect(commit.status).to eq("skipped")
-        expect(commit.yaml_errors).to be_nil
+        pipeline = service.execute(project, user,
+                                   ref: 'refs/tags/0_1',
+                                   before: '00000000',
+                                   after: '31das312',
+                                   commits: commits
+                                  )
+        expect(pipeline).to be_persisted
+        expect(pipeline.builds.any?).to be false
+        expect(pipeline.status).to eq("skipped")
+        expect(pipeline.yaml_errors).to be_nil
       end
     end
 
     it "skips build creation if there are already builds" do
-      allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file) { gitlab_ci_yaml }
+      allow_any_instance_of(Ci::Pipeline).to receive(:ci_yaml_file) { gitlab_ci_yaml }
 
       commits = [{ message: "message" }]
-      commit = service.execute(project, user,
-                               ref: 'refs/heads/master',
-                               before: '00000000',
-                               after: '31das312',
-                               commits: commits
-                              )
-      expect(commit).to be_persisted
-      expect(commit.builds.count(:all)).to eq(2)
+      pipeline = service.execute(project, user,
+                                 ref: 'refs/heads/master',
+                                 before: '00000000',
+                                 after: '31das312',
+                                 commits: commits
+                                )
+      expect(pipeline).to be_persisted
+      expect(pipeline.builds.count(:all)).to eq(2)
 
-      commit = service.execute(project, user,
-                               ref: 'refs/heads/master',
-                               before: '00000000',
-                               after: '31das312',
-                               commits: commits
-                              )
-      expect(commit).to be_persisted
-      expect(commit.builds.count(:all)).to eq(2)
+      pipeline = service.execute(project, user,
+                                 ref: 'refs/heads/master',
+                                 before: '00000000',
+                                 after: '31das312',
+                                 commits: commits
+                                )
+      expect(pipeline).to be_persisted
+      expect(pipeline.builds.count(:all)).to eq(2)
     end
 
     it "creates commit with failed status if yaml is invalid" do
-      stub_ci_commit_yaml_file('invalid: file')
+      stub_ci_pipeline_yaml_file('invalid: file')
 
       commits = [{ message: "some message" }]
 
-      commit = service.execute(project, user,
-                               ref: 'refs/tags/0_1',
-                               before: '00000000',
-                               after: '31das312',
-                               commits: commits
-                              )
+      pipeline = service.execute(project, user,
+                                 ref: 'refs/tags/0_1',
+                                 before: '00000000',
+                                 after: '31das312',
+                                 commits: commits
+                                )
 
-      expect(commit).to be_persisted
-      expect(commit.status).to eq("failed")
-      expect(commit.builds.any?).to be false
+      expect(pipeline).to be_persisted
+      expect(pipeline.status).to eq("failed")
+      expect(pipeline.builds.any?).to be false
     end
   end
 end
diff --git a/spec/services/create_deployment_service_spec.rb b/spec/services/create_deployment_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..654e441f3cd44f404d0d40facadf0fdcd5e3f69e
--- /dev/null
+++ b/spec/services/create_deployment_service_spec.rb
@@ -0,0 +1,119 @@
+require 'spec_helper'
+
+describe CreateDeploymentService, services: true do
+  let(:project) { create(:empty_project) }
+  let(:user) { create(:user) }
+
+  let(:service) { described_class.new(project, user, params) }
+
+  describe '#execute' do
+    let(:params) do
+      { environment: 'production',
+        ref: 'master',
+        tag: false,
+        sha: '97de212e80737a608d939f648d959671fb0a0142',
+      }
+    end
+
+    subject { service.execute }
+
+    context 'when no environments exist' do
+      it 'does create a new environment' do
+        expect { subject }.to change { Environment.count }.by(1)
+      end
+
+      it 'does create a deployment' do
+        expect(subject).to be_persisted
+      end
+    end
+
+    context 'when environment exist' do
+      before { create(:environment, project: project, name: 'production') }
+
+      it 'does not create a new environment' do
+        expect { subject }.not_to change { Environment.count }
+      end
+
+      it 'does create a deployment' do
+        expect(subject).to be_persisted
+      end
+    end
+
+    context 'for environment with invalid name' do
+      let(:params) do
+        { environment: 'name with spaces',
+          ref: 'master',
+          tag: false,
+          sha: '97de212e80737a608d939f648d959671fb0a0142',
+        }
+      end
+
+      it 'does not create a new environment' do
+        expect { subject }.not_to change { Environment.count }
+      end
+
+      it 'does not create a deployment' do
+        expect(subject).not_to be_persisted
+      end
+    end
+  end
+  
+  describe 'processing of builds' do
+    let(:environment) { nil }
+    
+    shared_examples 'does not create environment and deployment' do
+      it 'does not create a new environment' do
+        expect { subject }.not_to change { Environment.count }
+      end
+
+      it 'does not create a new deployment' do
+        expect { subject }.not_to change { Deployment.count }
+      end
+
+      it 'does not call a service' do
+        expect_any_instance_of(described_class).not_to receive(:execute)
+        subject
+      end
+    end
+
+    shared_examples 'does create environment and deployment' do
+      it 'does create a new environment' do
+        expect { subject }.to change { Environment.count }.by(1)
+      end
+
+      it 'does create a new deployment' do
+        expect { subject }.to change { Deployment.count }.by(1)
+      end
+
+      it 'does call a service' do
+        expect_any_instance_of(described_class).to receive(:execute)
+        subject
+      end
+    end
+
+    context 'without environment specified' do
+      let(:build) { create(:ci_build, project: project) }
+      
+      it_behaves_like 'does not create environment and deployment' do
+        subject { build.success }
+      end
+    end
+    
+    context 'when environment is specified' do
+      let(:pipeline) { create(:ci_pipeline, project: project) }
+      let(:build) { create(:ci_build, pipeline: pipeline, environment: 'production') }
+
+      context 'when build succeeds' do
+        it_behaves_like 'does create environment and deployment' do
+          subject { build.success }
+        end
+      end
+
+      context 'when build fails' do
+        it_behaves_like 'does not create environment and deployment' do
+          subject { build.drop }
+        end
+      end
+    end
+  end
+end
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index 5b48de51f9028779febcc228f26b0c978ce23b84..c569153fc429f0a87aa6554ee655790f19b4530c 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -139,11 +139,11 @@ describe GitPushService, services: true do
 
   describe "ES indexing" do
     before do
-      allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(true)
+      stub_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
     end
 
     after do
-      allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(false)
+      stub_application_setting(elasticsearch_search: false, elasticsearch_indexing: false)
     end
 
     it "triggers indexer" do
@@ -174,49 +174,6 @@ describe GitPushService, services: true do
     end
   end
 
-  describe "Updates main language" do
-    context "before push" do
-      it { expect(project.main_language).to eq(nil) }
-    end
-
-    context "after push" do
-      def execute
-        execute_service(project, user, @oldrev, @newrev, ref)
-      end
-
-      context "to master" do
-        let(:ref) { @ref }
-
-        context 'when main_language is nil' do
-          it 'obtains the language from the repository' do
-            expect(project.repository).to receive(:main_language)
-            execute
-          end
-
-          it 'sets the project main language' do
-            execute
-            expect(project.main_language).to eq("Ruby")
-          end
-        end
-
-        context 'when main_language is already set' do
-          it 'does not check the repository' do
-            execute # do an initial run to simulate lang being preset
-            expect(project.repository).not_to receive(:main_language)
-            execute
-          end
-        end
-      end
-
-      context "to other branch" do
-        let(:ref) { 'refs/heads/feature/branch' }
-
-        it { expect(project.main_language).to eq(nil) }
-      end
-    end
-  end
-
-
   describe "Updates git attributes" do
     context "for default branch" do
       it "calls the copy attributes method for the first push to the default branch" do
diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb
index 6aefb48a4e8ff82b410d3aa54ce9b8d0b4e7fab7..71a0b8e2a12b03c580f84a293e2eb01e2acef5a7 100644
--- a/spec/services/groups/create_service_spec.rb
+++ b/spec/services/groups/create_service_spec.rb
@@ -13,8 +13,8 @@ describe Groups::CreateService, services: true do
     end
 
     context "cannot create group with restricted visibility level" do
-      before { allow(current_application_settings).to receive(:restricted_visibility_levels).and_return([Gitlab::VisibilityLevel::PUBLIC]) }
-      it { is_expected.to_not be_persisted }
+      before { allow_any_instance_of(ApplicationSetting).to receive(:restricted_visibility_levels).and_return([Gitlab::VisibilityLevel::PUBLIC]) }
+      it { is_expected.not_to be_persisted }
     end
   end
 end
diff --git a/spec/services/issues/bulk_update_service_spec.rb b/spec/services/issues/bulk_update_service_spec.rb
index f16059a818fb4276a33a75e5b86be151e52f3362..4a689e64dc5d0641bcb0accd71381b2b050e5e9f 100644
--- a/spec/services/issues/bulk_update_service_spec.rb
+++ b/spec/services/issues/bulk_update_service_spec.rb
@@ -1,119 +1,265 @@
 require 'spec_helper'
 
 describe Issues::BulkUpdateService, services: true do
-  let(:issue) { create(:issue, project: @project) }
-
-  before do
-    @user = create :user
-    opts = {
-      name: "GitLab",
-      namespace: @user.namespace
-    }
-    @project = Projects::CreateService.new(@user, opts).execute
-  end
+  let(:user) { create(:user) }
+  let(:project) { Projects::CreateService.new(user, namespace: user.namespace, name: 'test').execute }
 
-  describe :close_issue do
+  let!(:result) { Issues::BulkUpdateService.new(project, user, params).execute }
 
-    before do
-      @issues = 5.times.collect do
-        create(:issue, project: @project)
-      end
-      @params = {
+  describe :close_issue do
+    let(:issues) { create_list(:issue, 5, project: project) }
+    let(:params) do
+      {
         state_event: 'close',
-        issues_ids: @issues.map(&:id).join(",")
+        issues_ids: issues.map(&:id).join(',')
       }
     end
 
-    it do
-      result = Issues::BulkUpdateService.new(@project, @user, @params).execute
+    it 'succeeds and returns the correct number of issues updated' do
       expect(result[:success]).to be_truthy
-      expect(result[:count]).to eq(@issues.count)
-
-      expect(@project.issues.opened).to be_empty
-      expect(@project.issues.closed).not_to be_empty
+      expect(result[:count]).to eq(issues.count)
     end
 
+    it 'closes all the issues passed' do
+      expect(project.issues.opened).to be_empty
+      expect(project.issues.closed).not_to be_empty
+    end
   end
 
   describe :reopen_issues do
-
-    before do
-      @issues = 5.times.collect do
-        create(:closed_issue, project: @project)
-      end
-      @params = {
+    let(:issues) { create_list(:closed_issue, 5, project: project) }
+    let(:params) do
+      {
         state_event: 'reopen',
-        issues_ids: @issues.map(&:id).join(",")
+        issues_ids: issues.map(&:id).join(',')
       }
     end
 
-    it do
-      result = Issues::BulkUpdateService.new(@project, @user, @params).execute
+    it 'succeeds and returns the correct number of issues updated' do
       expect(result[:success]).to be_truthy
-      expect(result[:count]).to eq(@issues.count)
-
-      expect(@project.issues.closed).to be_empty
-      expect(@project.issues.opened).not_to be_empty
+      expect(result[:count]).to eq(issues.count)
     end
 
+    it 'reopens all the issues passed' do
+      expect(project.issues.closed).to be_empty
+      expect(project.issues.opened).not_to be_empty
+    end
   end
 
-  describe :update_assignee do
+  describe 'updating assignee' do
+    let(:issue) do
+      create(:issue, project: project) { |issue| issue.update_attributes(assignee: user) }
+    end
 
-    before do
-      @new_assignee = create :user
-      @params = {
-        issues_ids: issue.id.to_s,
-        assignee_id: @new_assignee.id
+    let(:params) do
+      {
+        assignee_id: assignee_id,
+        issues_ids: issue.id.to_s
       }
     end
 
-    it do
-      result = Issues::BulkUpdateService.new(@project, @user, @params).execute
-      expect(result[:success]).to be_truthy
-      expect(result[:count]).to eq(1)
+    context 'when the new assignee ID is a valid user' do
+      let(:new_assignee) { create(:user) }
+      let(:assignee_id) { new_assignee.id }
 
-      expect(@project.issues.first.assignee).to eq(@new_assignee)
-    end
+      it 'succeeds' do
+        expect(result[:success]).to be_truthy
+        expect(result[:count]).to eq(1)
+      end
 
-    it 'allows mass-unassigning' do
-      @project.issues.first.update_attribute(:assignee, @new_assignee)
-      expect(@project.issues.first.assignee).not_to be_nil
+      it 'updates the assignee to the use ID passed' do
+        expect(issue.reload.assignee).to eq(new_assignee)
+      end
+    end
 
-      @params[:assignee_id] = -1
+    context 'when the new assignee ID is -1' do
+      let(:assignee_id) { -1 }
 
-      Issues::BulkUpdateService.new(@project, @user, @params).execute
-      expect(@project.issues.first.assignee).to be_nil
+      it 'unassigns the issues' do
+        expect(issue.reload.assignee).to be_nil
+      end
     end
 
-    it 'does not unassign when assignee_id is not present' do
-      @project.issues.first.update_attribute(:assignee, @new_assignee)
-      expect(@project.issues.first.assignee).not_to be_nil
-
-      @params[:assignee_id] = ''
+    context 'when the new assignee ID is not present' do
+      let(:assignee_id) { nil }
 
-      Issues::BulkUpdateService.new(@project, @user, @params).execute
-      expect(@project.issues.first.assignee).not_to be_nil
+      it 'does not unassign' do
+        expect(issue.reload.assignee).to eq(user)
+      end
     end
   end
 
-  describe :update_milestone do
+  describe 'updating milestones' do
+    let(:issue) { create(:issue, project: project) }
+    let(:milestone) { create(:milestone, project: project) }
 
-    before do
-      @milestone = create(:milestone, project: @project)
-      @params = {
+    let(:params) do
+      {
         issues_ids: issue.id.to_s,
-        milestone_id: @milestone.id
+        milestone_id: milestone.id
       }
     end
 
-    it do
-      result = Issues::BulkUpdateService.new(@project, @user, @params).execute
+    it 'succeeds' do
       expect(result[:success]).to be_truthy
       expect(result[:count]).to eq(1)
+    end
 
-      expect(@project.issues.first.milestone).to eq(@milestone)
+    it 'updates the issue milestone' do
+      expect(project.issues.first.milestone).to eq(milestone)
     end
   end
 
+  describe 'updating labels' do
+    def create_issue_with_labels(labels)
+      create(:issue, project: project) { |issue| issue.update_attributes(labels: labels) }
+    end
+
+    let(:bug) { create(:label, project: project) }
+    let(:regression) { create(:label, project: project) }
+    let(:merge_requests) { create(:label, project: project) }
+
+    let(:issue_all_labels) { create_issue_with_labels([bug, regression, merge_requests]) }
+    let(:issue_bug_and_regression) { create_issue_with_labels([bug, regression]) }
+    let(:issue_bug_and_merge_requests) { create_issue_with_labels([bug, merge_requests]) }
+    let(:issue_no_labels) { create(:issue, project: project) }
+    let(:issues) { [issue_all_labels, issue_bug_and_regression, issue_bug_and_merge_requests, issue_no_labels] }
+
+    let(:labels) { [] }
+    let(:add_labels) { [] }
+    let(:remove_labels) { [] }
+
+    let(:params) do
+      {
+        label_ids: labels.map(&:id),
+        add_label_ids: add_labels.map(&:id),
+        remove_label_ids: remove_labels.map(&:id),
+        issues_ids: issues.map(&:id).join(',')
+      }
+    end
+
+    context 'when label_ids are passed' do
+      let(:issues) { [issue_all_labels, issue_no_labels] }
+      let(:labels) { [bug, regression] }
+
+      it 'updates the labels of all issues passed to the labels passed' do
+        expect(issues.map(&:reload).map(&:label_ids)).to all(eq(labels.map(&:id)))
+      end
+
+      it 'does not update issues not passed in' do
+        expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+      end
+
+      context 'when those label IDs are empty' do
+        let(:labels) { [] }
+
+        it 'updates the issues passed to have no labels' do
+          expect(issues.map(&:reload).map(&:label_ids)).to all(be_empty)
+        end
+      end
+    end
+
+    context 'when add_label_ids are passed' do
+      let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] }
+      let(:add_labels) { [bug, regression, merge_requests] }
+
+      it 'adds those label IDs to all issues passed' do
+        expect(issues.map(&:reload).map(&:label_ids)).to all(include(*add_labels.map(&:id)))
+      end
+
+      it 'does not update issues not passed in' do
+        expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+      end
+    end
+
+    context 'when remove_label_ids are passed' do
+      let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] }
+      let(:remove_labels) { [bug, regression, merge_requests] }
+
+      it 'removes those label IDs from all issues passed' do
+        expect(issues.map(&:reload).map(&:label_ids)).to all(be_empty)
+      end
+
+      it 'does not update issues not passed in' do
+        expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+      end
+    end
+
+    context 'when add_label_ids and remove_label_ids are passed' do
+      let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] }
+      let(:add_labels) { [bug] }
+      let(:remove_labels) { [merge_requests] }
+
+      it 'adds the label IDs to all issues passed' do
+        expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id))
+      end
+
+      it 'removes the label IDs from all issues passed' do
+        expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(merge_requests.id)
+      end
+
+      it 'does not update issues not passed in' do
+        expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+      end
+    end
+
+    context 'when add_label_ids and label_ids are passed' do
+      let(:issues) { [issue_all_labels, issue_bug_and_regression, issue_bug_and_merge_requests] }
+      let(:labels) { [merge_requests] }
+      let(:add_labels) { [regression] }
+
+      it 'adds the label IDs to all issues passed' do
+        expect(issues.map(&:reload).map(&:label_ids)).to all(include(regression.id))
+      end
+
+      it 'ignores the label IDs parameter' do
+        expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id))
+      end
+
+      it 'does not update issues not passed in' do
+        expect(issue_no_labels.label_ids).to be_empty
+      end
+    end
+
+    context 'when remove_label_ids and label_ids are passed' do
+      let(:issues) { [issue_no_labels, issue_bug_and_regression] }
+      let(:labels) { [merge_requests] }
+      let(:remove_labels) { [regression] }
+
+      it 'remove the label IDs from all issues passed' do
+        expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(regression.id)
+      end
+
+      it 'ignores the label IDs parameter' do
+        expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(merge_requests.id)
+      end
+
+      it 'does not update issues not passed in' do
+        expect(issue_all_labels.label_ids).to contain_exactly(bug.id, regression.id, merge_requests.id)
+      end
+    end
+
+    context 'when add_label_ids, remove_label_ids, and label_ids are passed' do
+      let(:issues) { [issue_bug_and_merge_requests, issue_no_labels] }
+      let(:labels) { [regression] }
+      let(:add_labels) { [bug] }
+      let(:remove_labels) { [merge_requests] }
+
+      it 'adds the label IDs to all issues passed' do
+        expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id))
+      end
+
+      it 'removes the label IDs from all issues passed' do
+        expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(merge_requests.id)
+      end
+
+      it 'ignores the label IDs parameter' do
+        expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(regression.id)
+      end
+
+      it 'does not update issues not passed in' do
+        expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+      end
+    end
+  end
 end
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index ac28b6f71f9a9465c2e864695bf1ec4077975790..1ee9f3aae4dcb17aaf754121e33e75c4381df444 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -54,8 +54,8 @@ describe Issues::CreateService, services: true do
             label_ids: [label.id] }
         end
 
-        it 'does not assign label'do
-          expect(issue.labels).to_not include label
+        it 'does not assign label' do
+          expect(issue.labels).not_to include label
         end
       end
 
@@ -69,7 +69,7 @@ describe Issues::CreateService, services: true do
         end
 
         it 'does not assign milestone' do
-          expect(issue.milestone).to_not eq milestone
+          expect(issue.milestone).not_to eq milestone
         end
       end
     end
diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb
index c15e26189a518a7a03d204b12779c519ca649495..93bf0f649634e4d0cb4e338841e3626abecd4d12 100644
--- a/spec/services/issues/move_service_spec.rb
+++ b/spec/services/issues/move_service_spec.rb
@@ -39,6 +39,7 @@ describe Issues::MoveService, services: true do
       let!(:milestone2) do
         create(:milestone, project_id: new_project.id, title: 'v9.0')
       end
+      let!(:award_emoji) { create(:award_emoji, awardable: old_issue) }
 
       let!(:new_issue) { move_service.execute(old_issue, new_project) }
     end
@@ -115,6 +116,10 @@ describe Issues::MoveService, services: true do
         it 'preserves create time' do
           expect(old_issue.created_at).to eq new_issue.created_at
         end
+
+        it 'moves the award emoji' do
+          expect(old_issue.award_emoji.first.name).to eq new_issue.reload.award_emoji.first.name
+        end
       end
 
       context 'issue with notes' do
@@ -194,10 +199,10 @@ describe Issues::MoveService, services: true do
           include_context 'issue move executed'
 
           it 'rewrites uploads in description' do
-            expect(new_issue.description).to_not eq description
+            expect(new_issue.description).not_to eq description
             expect(new_issue.description)
               .to match(/Text and #{FileUploader::MARKDOWN_PATTERN}/)
-            expect(new_issue.description).to_not include uploader.secret
+            expect(new_issue.description).not_to include uploader.secret
           end
         end
       end
@@ -231,7 +236,7 @@ describe Issues::MoveService, services: true do
 
       context 'user is reporter in both projects' do
         include_context 'user can move issue'
-        it { expect { move }.to_not raise_error }
+        it { expect { move }.not_to raise_error }
       end
 
       context 'user is reporter only in new project' do
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index be19be17151cbd170999a3ae63dd9f26f9862c19..dacbcd8fb46e07c5ae61a41490f2418cce8ab99a 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
 require 'spec_helper'
 
 describe Issues::UpdateService, services: true do
@@ -273,5 +274,50 @@ describe Issues::UpdateService, services: true do
         end
       end
     end
+
+    context 'updating labels' do
+      let(:label3) { create(:label, project: project) }
+      let(:result) { Issues::UpdateService.new(project, user, params).execute(issue).reload }
+
+      context 'when add_label_ids and label_ids are passed' do
+        let(:params) { { label_ids: [label.id], add_label_ids: [label3.id] } }
+
+        it 'ignores the label_ids parameter' do
+          expect(result.label_ids).not_to include(label.id)
+        end
+
+        it 'adds the passed labels' do
+          expect(result.label_ids).to include(label3.id)
+        end
+      end
+
+      context 'when remove_label_ids and label_ids are passed' do
+        let(:params) { { label_ids: [], remove_label_ids: [label.id] } }
+
+        before { issue.update_attributes(labels: [label, label3]) }
+
+        it 'ignores the label_ids parameter' do
+          expect(result.label_ids).not_to be_empty
+        end
+
+        it 'removes the passed labels' do
+          expect(result.label_ids).not_to include(label.id)
+        end
+      end
+
+      context 'when add_label_ids and remove_label_ids are passed' do
+        let(:params) { { add_label_ids: [label3.id], remove_label_ids: [label.id] } }
+
+        before { issue.update_attributes(labels: [label]) }
+
+        it 'adds the passed labels' do
+          expect(result.label_ids).to include(label3.id)
+        end
+
+        it 'removes the passed labels' do
+          expect(result.label_ids).not_to include(label.id)
+        end
+      end
+    end
   end
 end
diff --git a/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb b/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
index f70716c9d1900e82d0c8f2166425cda041c3747f..dd656c3bbb7c149ebaf4d26fd9aceb8c918b25d1 100644
--- a/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
+++ b/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
@@ -6,7 +6,7 @@ describe MergeRequests::AddTodoWhenBuildFailsService do
   let(:merge_request) { create(:merge_request) }
   let(:project) { create(:project) }
   let(:sha) { '1234567890abcdef1234567890abcdef12345678' }
-  let(:ci_commit) { create(:ci_commit_with_one_job, ref: merge_request.source_branch, project: project, sha: sha) }
+  let(:pipeline) { create(:ci_pipeline_with_one_job, ref: merge_request.source_branch, project: project, sha: sha) }
   let(:service) { MergeRequests::AddTodoWhenBuildFailsService.new(project, user, commit_message: 'Awesome message') }
   let(:todo_service) { TodoService.new }
 
@@ -17,13 +17,13 @@ describe MergeRequests::AddTodoWhenBuildFailsService do
   end
 
   before do
-    allow_any_instance_of(MergeRequest).to receive(:ci_commit).and_return(ci_commit)
+    allow_any_instance_of(MergeRequest).to receive(:pipeline).and_return(pipeline)
     allow(service).to receive(:todo_service).and_return(todo_service)
   end
 
   describe '#execute' do
     context 'commit status with ref' do
-      let(:commit_status) { create(:generic_commit_status, ref: merge_request.source_branch, commit: ci_commit) }
+      let(:commit_status) { create(:generic_commit_status, ref: merge_request.source_branch, pipeline: pipeline) }
 
       it 'notifies the todo service' do
         expect(todo_service).to receive(:merge_request_build_failed).with(merge_request)
@@ -52,7 +52,7 @@ describe MergeRequests::AddTodoWhenBuildFailsService do
 
   describe '#close' do
     context 'commit status with ref' do
-      let(:commit_status) { create(:generic_commit_status, ref: merge_request.source_branch, commit: ci_commit) }
+      let(:commit_status) { create(:generic_commit_status, ref: merge_request.source_branch, pipeline: pipeline) }
 
       it 'notifies the todo service' do
         expect(todo_service).to receive(:merge_request_build_retried).with(merge_request)
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index 2b3e92b4c7af574de3363ded7068b4e57036a603..2da600c34bb7eac73c08e9cb356b464ca6571565 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -96,5 +96,13 @@ describe MergeRequests::MergeService, services: true do
         expect(merge_request.merge_error).not_to be_empty
       end
     end
+
+    context 'fast forward merge request' do
+      it 'returns true when fast forward is enabled' do
+        allow(project).to receive(:merge_requests_ff_only_enabled) { true }
+
+        expect(service.hooks_validation_pass?(merge_request)).to be_truthy
+      end
+    end
   end
 end
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 52a302e0e1a9b746a4380f8d8d8aa110960d6926..4da8146e3d6a41dd59a283bd4d5a952f9d4e939c 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
@@ -1,8 +1,8 @@
 require 'spec_helper'
 
 describe MergeRequests::MergeWhenBuildSucceedsService do
-  let(:user)          { create(:user) }
-  let(:merge_request) { create(:merge_request) }
+  let(:user) { create(:user) }
+  let(:project) { create(:project) }
 
   let(:mr_merge_if_green_enabled) do
     create(:merge_request, merge_when_build_succeeds: true, merge_user: user,
@@ -10,14 +10,18 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
                            source_project: project, target_project: project, state: "opened")
   end
 
-  let(:project) { create(:project) }
-  let(:ci_commit) { create(:ci_commit_with_one_job, ref: mr_merge_if_green_enabled.source_branch, project: project) }
+  let(:pipeline) { create(:ci_pipeline_with_one_job, ref: mr_merge_if_green_enabled.source_branch, project: project) }
   let(:service) { MergeRequests::MergeWhenBuildSucceedsService.new(project, user, commit_message: 'Awesome message') }
 
   describe "#execute" do
+    let(:merge_request) do
+      create(:merge_request, target_project: project, source_project: project,
+                             source_branch: "feature", target_branch: 'master')
+    end
+
     context 'first time enabling' do
       before do
-        allow(merge_request).to receive(:ci_commit).and_return(ci_commit)
+        allow(merge_request).to receive(:pipeline).and_return(pipeline)
         service.execute(merge_request)
       end
 
@@ -39,9 +43,9 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
       let(:build)   { create(:ci_build, ref: mr_merge_if_green_enabled.source_branch) }
 
       before do
-        allow(mr_merge_if_green_enabled).to receive(:ci_commit).and_return(ci_commit)
+        allow(mr_merge_if_green_enabled).to receive(:pipeline).and_return(pipeline)
         allow(mr_merge_if_green_enabled).to receive(:mergeable?).and_return(true)
-        allow(ci_commit).to receive(:success?).and_return(true)
+        allow(pipeline).to receive(:success?).and_return(true)
       end
 
       it 'updates the merge params' do
@@ -58,8 +62,8 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
       let(:build)     { create(:ci_build, ref: mr_merge_if_green_enabled.source_branch, status: "success") }
 
       it "merges all merge requests with merge when build succeeds enabled" do
-        allow_any_instance_of(MergeRequest).to receive(:ci_commit).and_return(ci_commit)
-        allow(ci_commit).to receive(:success?).and_return(true)
+        allow_any_instance_of(MergeRequest).to receive(:pipeline).and_return(pipeline)
+        allow(pipeline).to receive(:success?).and_return(true)
 
         expect(MergeWorker).to receive(:perform_async)
         service.trigger(build)
@@ -71,11 +75,11 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
       let(:build)     { create(:ci_build, ref: mr_merge_if_green_enabled.source_branch, status: "success") }
 
       it "merges all merge requests with merge when build succeeds enabled" do
-        allow_any_instance_of(MergeRequest).to receive(:ci_commit).and_return(ci_commit)
-        allow(ci_commit).to receive(:success?).and_return(true)
+        allow_any_instance_of(MergeRequest).to receive(:pipeline).and_return(pipeline)
+        allow(pipeline).to receive(:success?).and_return(true)
         allow(old_build).to receive(:sha).and_return('1234abcdef')
 
-        expect(MergeWorker).to_not receive(:perform_async)
+        expect(MergeWorker).not_to receive(:perform_async)
         service.trigger(old_build)
       end
     end
@@ -88,16 +92,16 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
       it "doesn't merge a requests for status on other branch" do
         allow(project.repository).to receive(:branch_names_contains).with(commit_status.sha).and_return([])
 
-        expect(MergeWorker).to_not receive(:perform_async)
+        expect(MergeWorker).not_to receive(:perform_async)
         service.trigger(commit_status)
       end
 
       it 'discovers branches and merges all merge requests when status is success' do
         allow(project.repository).to receive(:branch_names_contains).
           with(commit_status.sha).and_return([mr_merge_if_green_enabled.source_branch])
-        allow(ci_commit).to receive(:success?).and_return(true)
-        allow_any_instance_of(MergeRequest).to receive(:ci_commit).and_return(ci_commit)
-        allow(ci_commit).to receive(:success?).and_return(true)
+        allow(pipeline).to receive(:success?).and_return(true)
+        allow_any_instance_of(MergeRequest).to receive(:pipeline).and_return(pipeline)
+        allow(pipeline).to receive(:success?).and_return(true)
 
         expect(MergeWorker).to receive(:perform_async)
         service.trigger(commit_status)
@@ -106,23 +110,23 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
 
     context 'properly handles multiple stages' do
       let(:ref) { mr_merge_if_green_enabled.source_branch }
-      let(:build) { create(:ci_build, commit: ci_commit, ref: ref, name: 'build', stage: 'build') }
-      let(:test) { create(:ci_build, commit: ci_commit, ref: ref, name: 'test', stage: 'test') }
+      let(:build) { create(:ci_build, pipeline: pipeline, ref: ref, name: 'build', stage: 'build') }
+      let(:test) { create(:ci_build, pipeline: pipeline, ref: ref, name: 'test', stage: 'test') }
 
       before do
         # This behavior of MergeRequest: we instantiate a new object
-        allow_any_instance_of(MergeRequest).to receive(:ci_commit).and_wrap_original do
-          Ci::Commit.find(ci_commit.id)
+        allow_any_instance_of(MergeRequest).to receive(:pipeline).and_wrap_original do
+          Ci::Pipeline.find(pipeline.id)
         end
 
         # We create test after the build
-        allow(ci_commit).to receive(:create_next_builds).and_wrap_original do
+        allow(pipeline).to receive(:create_next_builds).and_wrap_original do
           test
         end
       end
 
       it "doesn't merge if some stages failed" do
-        expect(MergeWorker).to_not receive(:perform_async)
+        expect(MergeWorker).not_to receive(:perform_async)
         build.success
         test.drop
       end
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index ff23f13e1cb3569a9e959670fc70e2ec5605d5ea..35f576874b82711f13fc7c5856067e143131c6db 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -14,7 +14,7 @@ describe Notes::CreateService, services: true do
           noteable_type: 'Issue',
           noteable_id: issue.id
         }
-        
+
         @note = Notes::CreateService.new(project, user, opts).execute
       end
 
@@ -28,18 +28,16 @@ describe Notes::CreateService, services: true do
       project.team << [user, :master]
     end
 
-    it "creates emoji note" do
+    it "creates an award emoji" do
       opts = {
         note: ':smile: ',
         noteable_type: 'Issue',
         noteable_id: issue.id
       }
+      note = Notes::CreateService.new(project, user, opts).execute
 
-      @note = Notes::CreateService.new(project, user, opts).execute
-
-      expect(@note).to be_valid
-      expect(@note.note).to eq('smile')
-      expect(@note.is_award).to be_truthy
+      expect(note).to be_valid
+      expect(note.name).to eq('smile')
     end
 
     it "creates regular note if emoji name is invalid" do
@@ -48,12 +46,22 @@ describe Notes::CreateService, services: true do
         noteable_type: 'Issue',
         noteable_id: issue.id
       }
+      note = Notes::CreateService.new(project, user, opts).execute
+
+      expect(note).to be_valid
+      expect(note.note).to eq(opts[:note])
+    end
+
+    it "normalizes the emoji name" do
+      opts = {
+        note: ':+1:',
+        noteable_type: 'Issue',
+        noteable_id: issue.id
+      }
 
-      @note = Notes::CreateService.new(project, user, opts).execute
+      expect_any_instance_of(TodoService).to receive(:new_award_emoji).with(issue, user)
 
-      expect(@note).to be_valid
-      expect(@note.note).to eq(opts[:note])
-      expect(@note.is_award).to be_falsy
+      Notes::CreateService.new(project, user, opts).execute
     end
   end
 end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index cef5e0d86596082f9774d6274fded64e63d6556b..e871a103d42a96b3b513c072af434da80090b2eb 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -72,6 +72,7 @@ describe NotificationService, services: true do
           should_not_email(@u_disabled)
           should_not_email(@unsubscriber)
           should_not_email(@u_outsider_mentioned)
+          should_not_email(@u_lazy_participant)
         end
 
         it 'filters out "mentioned in" notes' do
@@ -80,6 +81,20 @@ describe NotificationService, services: true do
           expect(Notify).not_to receive(:note_issue_email)
           notification.new_note(mentioned_note)
         end
+
+        context 'participating' do
+          context 'by note' do
+            before do
+              ActionMailer::Base.deliveries.clear
+              note.author = @u_lazy_participant
+              note.save
+              notification.new_note(note)
+            end
+
+
+            it { should_not_email(@u_lazy_participant) }
+          end
+        end
       end
 
       describe 'new note on issue in project that belongs to a group' do
@@ -106,6 +121,7 @@ describe NotificationService, services: true do
           should_not_email(note.author)
           should_not_email(@u_participating)
           should_not_email(@u_disabled)
+          should_not_email(@u_lazy_participant)
         end
       end
     end
@@ -116,12 +132,14 @@ describe NotificationService, services: true do
       let(:assignee) { create(:user) }
       let(:non_member) { create(:user) }
       let(:member) { create(:user) }
+      let(:guest) { create(:user) }
       let(:admin) { create(:admin) }
       let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee) }
       let(:note) { create(:note_on_issue, noteable: confidential_issue, project: project, note: "#{author.to_reference} #{assignee.to_reference} #{non_member.to_reference} #{member.to_reference} #{admin.to_reference}") }
 
       it 'filters out users that can not read the issue' do
         project.team << [member, :developer]
+        project.team << [guest, :guest]
 
         expect(SentNotification).to receive(:record).with(confidential_issue, any_args).exactly(4).times
 
@@ -130,6 +148,7 @@ describe NotificationService, services: true do
         notification.new_note(note)
 
         should_not_email(non_member)
+        should_not_email(guest)
         should_email(author)
         should_email(assignee)
         should_email(member)
@@ -235,6 +254,7 @@ describe NotificationService, services: true do
           should_not_email(note.author)
           should_not_email(@u_participating)
           should_not_email(@u_disabled)
+          should_not_email(@u_lazy_participant)
         end
 
         it do
@@ -248,10 +268,11 @@ describe NotificationService, services: true do
           should_not_email(note.author)
           should_not_email(@u_participating)
           should_not_email(@u_disabled)
+          should_not_email(@u_lazy_participant)
         end
 
         it do
-          @u_committer.update_attributes(notification_level: :mention)
+          @u_committer = create_global_setting_for(@u_committer, :mention)
           notification.new_note(note)
           should_not_email(@u_committer)
         end
@@ -280,10 +301,11 @@ describe NotificationService, services: true do
         should_not_email(@u_mentioned)
         should_not_email(@u_participating)
         should_not_email(@u_disabled)
+        should_not_email(@u_lazy_participant)
       end
 
       it do
-        issue.assignee.update_attributes(notification_level: :mention)
+        create_global_setting_for(issue.assignee, :mention)
         notification.new_issue(issue, @u_disabled)
 
         should_not_email(issue.assignee)
@@ -303,17 +325,20 @@ describe NotificationService, services: true do
         let(:assignee) { create(:user) }
         let(:non_member) { create(:user) }
         let(:member) { create(:user) }
+        let(:guest) { create(:user) }
         let(:admin) { create(:admin) }
         let(:confidential_issue) { create(:issue, :confidential, project: project, title: 'Confidential issue', author: author, assignee: assignee) }
 
         it "emails subscribers of the issue's labels that can read the issue" do
           project.team << [member, :developer]
+          project.team << [guest, :guest]
 
           label = create(:label, issues: [confidential_issue])
           label.toggle_subscription(non_member)
           label.toggle_subscription(author)
           label.toggle_subscription(assignee)
           label.toggle_subscription(member)
+          label.toggle_subscription(guest)
           label.toggle_subscription(admin)
 
           ActionMailer::Base.deliveries.clear
@@ -322,6 +347,7 @@ describe NotificationService, services: true do
 
           should_not_email(non_member)
           should_not_email(author)
+          should_not_email(guest)
           should_email(assignee)
           should_email(member)
           should_email(admin)
@@ -341,6 +367,7 @@ describe NotificationService, services: true do
         should_not_email(@unsubscriber)
         should_not_email(@u_participating)
         should_not_email(@u_disabled)
+        should_not_email(@u_lazy_participant)
       end
 
       it 'emails previous assignee even if he has the "on mention" notif level' do
@@ -356,6 +383,7 @@ describe NotificationService, services: true do
         should_not_email(@unsubscriber)
         should_not_email(@u_participating)
         should_not_email(@u_disabled)
+        should_not_email(@u_lazy_participant)
       end
 
       it 'emails new assignee even if he has the "on mention" notif level' do
@@ -371,6 +399,7 @@ describe NotificationService, services: true do
         should_not_email(@unsubscriber)
         should_not_email(@u_participating)
         should_not_email(@u_disabled)
+        should_not_email(@u_lazy_participant)
       end
 
       it 'emails new assignee' do
@@ -386,6 +415,7 @@ describe NotificationService, services: true do
         should_not_email(@unsubscriber)
         should_not_email(@u_participating)
         should_not_email(@u_disabled)
+        should_not_email(@u_lazy_participant)
       end
 
       it 'does not email new assignee if they are the current user' do
@@ -401,6 +431,35 @@ describe NotificationService, services: true do
         should_not_email(@unsubscriber)
         should_not_email(@u_participating)
         should_not_email(@u_disabled)
+        should_not_email(@u_lazy_participant)
+      end
+
+      context 'participating' do
+        context 'by assignee' do
+          before do
+            issue.update_attribute(:assignee, @u_lazy_participant)
+            notification.reassigned_issue(issue, @u_disabled)
+          end
+
+          it { should_email(@u_lazy_participant) }
+        end
+
+        context 'by note' do
+          let!(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: 'anything', author: @u_lazy_participant) }
+
+          before { notification.reassigned_issue(issue, @u_disabled) }
+
+          it { should_email(@u_lazy_participant) }
+        end
+
+        context 'by author' do
+          before do
+            issue.author = @u_lazy_participant
+            notification.reassigned_issue(issue, @u_disabled)
+          end
+
+          it { should_email(@u_lazy_participant) }
+        end
       end
     end
 
@@ -438,6 +497,7 @@ describe NotificationService, services: true do
         let(:assignee) { create(:user) }
         let(:non_member) { create(:user) }
         let(:member) { create(:user) }
+        let(:guest) { create(:user) }
         let(:admin) { create(:admin) }
         let(:confidential_issue) { create(:issue, :confidential, project: project, title: 'Confidential issue', author: author, assignee: assignee) }
         let!(:label_1) { create(:label, issues: [confidential_issue]) }
@@ -445,11 +505,13 @@ describe NotificationService, services: true do
 
         it "emails subscribers of the issue's labels that can read the issue" do
           project.team << [member, :developer]
+          project.team << [guest, :guest]
 
           label_2.toggle_subscription(non_member)
           label_2.toggle_subscription(author)
           label_2.toggle_subscription(assignee)
           label_2.toggle_subscription(member)
+          label_2.toggle_subscription(guest)
           label_2.toggle_subscription(admin)
 
           ActionMailer::Base.deliveries.clear
@@ -457,6 +519,7 @@ describe NotificationService, services: true do
           notification.relabeled_issue(confidential_issue, [label_2], @u_disabled)
 
           should_not_email(non_member)
+          should_not_email(guest)
           should_email(author)
           should_email(assignee)
           should_email(member)
@@ -479,6 +542,35 @@ describe NotificationService, services: true do
         should_not_email(@unsubscriber)
         should_not_email(@u_participating)
         should_not_email(@u_disabled)
+        should_not_email(@u_lazy_participant)
+      end
+
+      context 'participating' do
+        context 'by assignee' do
+          before do
+            issue.update_attribute(:assignee, @u_lazy_participant)
+            notification.close_issue(issue, @u_disabled)
+          end
+
+          it { should_email(@u_lazy_participant) }
+        end
+
+        context 'by note' do
+          let!(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: 'anything', author: @u_lazy_participant) }
+
+          before { notification.close_issue(issue, @u_disabled) }
+
+          it { should_email(@u_lazy_participant) }
+        end
+
+        context 'by author' do
+          before do
+            issue.author = @u_lazy_participant
+            notification.close_issue(issue, @u_disabled)
+          end
+
+          it { should_email(@u_lazy_participant) }
+        end
       end
     end
 
@@ -495,6 +587,35 @@ describe NotificationService, services: true do
         should_email(@watcher_and_subscriber)
         should_not_email(@unsubscriber)
         should_not_email(@u_participating)
+        should_not_email(@u_lazy_participant)
+      end
+
+      context 'participating' do
+        context 'by assignee' do
+          before do
+            issue.update_attribute(:assignee, @u_lazy_participant)
+            notification.reopen_issue(issue, @u_disabled)
+          end
+
+          it { should_email(@u_lazy_participant) }
+        end
+
+        context 'by note' do
+          let!(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: 'anything', author: @u_lazy_participant) }
+
+          before { notification.reopen_issue(issue, @u_disabled) }
+
+          it { should_email(@u_lazy_participant) }
+        end
+
+        context 'by author' do
+          before do
+            issue.author = @u_lazy_participant
+            notification.reopen_issue(issue, @u_disabled)
+          end
+
+          it { should_email(@u_lazy_participant) }
+        end
       end
     end
   end
@@ -520,6 +641,7 @@ describe NotificationService, services: true do
         should_email(@u_guest_watcher)
         should_not_email(@u_participating)
         should_not_email(@u_disabled)
+        should_not_email(@u_lazy_participant)
       end
 
       it "emails subscribers of the merge request's labels" do
@@ -530,6 +652,36 @@ describe NotificationService, services: true do
 
         should_email(subscriber)
       end
+
+
+      context 'participating' do
+        context 'by assignee' do
+          before do
+            merge_request.update_attribute(:assignee, @u_lazy_participant)
+            notification.new_merge_request(merge_request, @u_disabled)
+          end
+
+          it { should_email(@u_lazy_participant) }
+        end
+
+        context 'by note' do
+          let!(:note) { create(:note_on_issue, noteable: merge_request, project_id: project.id, note: 'anything', author: @u_lazy_participant) }
+
+          before { notification.new_merge_request(merge_request, @u_disabled) }
+
+          it { should_email(@u_lazy_participant) }
+        end
+
+        context 'by author' do
+          before do
+            merge_request.author = @u_lazy_participant
+            merge_request.save
+            notification.new_merge_request(merge_request, @u_disabled)
+          end
+
+          it { should_not_email(@u_lazy_participant) }
+        end
+      end
     end
 
     describe '#reassigned_merge_request' do
@@ -545,6 +697,36 @@ describe NotificationService, services: true do
         should_not_email(@unsubscriber)
         should_not_email(@u_participating)
         should_not_email(@u_disabled)
+        should_not_email(@u_lazy_participant)
+      end
+
+      context 'participating' do
+        context 'by assignee' do
+          before do
+            merge_request.update_attribute(:assignee, @u_lazy_participant)
+            notification.reassigned_merge_request(merge_request, @u_disabled)
+          end
+
+          it { should_email(@u_lazy_participant) }
+        end
+
+        context 'by note' do
+          let!(:note) { create(:note_on_issue, noteable: merge_request, project_id: project.id, note: 'anything', author: @u_lazy_participant) }
+
+          before { notification.reassigned_merge_request(merge_request, @u_disabled) }
+
+          it { should_email(@u_lazy_participant) }
+        end
+
+        context 'by author' do
+          before do
+            merge_request.author = @u_lazy_participant
+            merge_request.save
+            notification.reassigned_merge_request(merge_request, @u_disabled)
+          end
+
+          it { should_email(@u_lazy_participant) }
+        end
       end
     end
 
@@ -572,6 +754,7 @@ describe NotificationService, services: true do
         should_not_email(@watcher_and_subscriber)
         should_not_email(@unsubscriber)
         should_not_email(@u_participating)
+        should_not_email(@u_lazy_participant)
         should_not_email(subscriber_to_label)
         should_email(subscriber_to_label2)
       end
@@ -590,6 +773,36 @@ describe NotificationService, services: true do
         should_not_email(@unsubscriber)
         should_not_email(@u_participating)
         should_not_email(@u_disabled)
+        should_not_email(@u_lazy_participant)
+      end
+
+      context 'participating' do
+        context 'by assignee' do
+          before do
+            merge_request.update_attribute(:assignee, @u_lazy_participant)
+            notification.close_mr(merge_request, @u_disabled)
+          end
+
+          it { should_email(@u_lazy_participant) }
+        end
+
+        context 'by note' do
+          let!(:note) { create(:note_on_issue, noteable: merge_request, project_id: project.id, note: 'anything', author: @u_lazy_participant) }
+
+          before { notification.close_mr(merge_request, @u_disabled) }
+
+          it { should_email(@u_lazy_participant) }
+        end
+
+        context 'by author' do
+          before do
+            merge_request.author = @u_lazy_participant
+            merge_request.save
+            notification.close_mr(merge_request, @u_disabled)
+          end
+
+          it { should_email(@u_lazy_participant) }
+        end
       end
     end
 
@@ -606,6 +819,36 @@ describe NotificationService, services: true do
         should_not_email(@unsubscriber)
         should_not_email(@u_participating)
         should_not_email(@u_disabled)
+        should_not_email(@u_lazy_participant)
+      end
+
+      context 'participating' do
+        context 'by assignee' do
+          before do
+            merge_request.update_attribute(:assignee, @u_lazy_participant)
+            notification.merge_mr(merge_request, @u_disabled)
+          end
+
+          it { should_email(@u_lazy_participant) }
+        end
+
+        context 'by note' do
+          let!(:note) { create(:note_on_issue, noteable: merge_request, project_id: project.id, note: 'anything', author: @u_lazy_participant) }
+
+          before { notification.merge_mr(merge_request, @u_disabled) }
+
+          it { should_email(@u_lazy_participant) }
+        end
+
+        context 'by author' do
+          before do
+            merge_request.author = @u_lazy_participant
+            merge_request.save
+            notification.merge_mr(merge_request, @u_disabled)
+          end
+
+          it { should_email(@u_lazy_participant) }
+        end
       end
     end
 
@@ -622,6 +865,36 @@ describe NotificationService, services: true do
         should_not_email(@unsubscriber)
         should_not_email(@u_participating)
         should_not_email(@u_disabled)
+        should_not_email(@u_lazy_participant)
+      end
+
+      context 'participating' do
+        context 'by assignee' do
+          before do
+            merge_request.update_attribute(:assignee, @u_lazy_participant)
+            notification.reopen_mr(merge_request, @u_disabled)
+          end
+
+          it { should_email(@u_lazy_participant) }
+        end
+
+        context 'by note' do
+          let!(:note) { create(:note_on_issue, noteable: merge_request, project_id: project.id, note: 'anything', author: @u_lazy_participant) }
+
+          before { notification.reopen_mr(merge_request, @u_disabled) }
+
+          it { should_email(@u_lazy_participant) }
+        end
+
+        context 'by author' do
+          before do
+            merge_request.author = @u_lazy_participant
+            merge_request.save
+            notification.reopen_mr(merge_request, @u_disabled)
+          end
+
+          it { should_email(@u_lazy_participant) }
+        end
       end
     end
   end
@@ -640,6 +913,7 @@ describe NotificationService, services: true do
 
         should_email(@u_watcher)
         should_email(@u_participating)
+        should_email(@u_lazy_participant)
         should_not_email(@u_guest_watcher)
         should_not_email(@u_disabled)
       end
@@ -647,14 +921,19 @@ describe NotificationService, services: true do
   end
 
   def build_team(project)
-    @u_watcher = create(:user, notification_level: :watch)
-    @u_participating = create(:user, notification_level: :participating)
-    @u_participant_mentioned = create(:user, username: 'participant', notification_level: :participating)
-    @u_disabled = create(:user, notification_level: :disabled)
-    @u_mentioned = create(:user, username: 'mention', notification_level: :mention)
-    @u_committer = create(:user, username: 'committer')
-    @u_not_mentioned = create(:user, username: 'regular', notification_level: :participating)
-    @u_outsider_mentioned = create(:user, username: 'outsider')
+    @u_watcher               = create_global_setting_for(create(:user), :watch)
+    @u_participating         = create_global_setting_for(create(:user), :participating)
+    @u_participant_mentioned = create_global_setting_for(create(:user, username: 'participant'), :participating)
+    @u_disabled              = create_global_setting_for(create(:user), :disabled)
+    @u_mentioned             = create_global_setting_for(create(:user, username: 'mention'), :mention)
+    @u_committer             = create(:user, username: 'committer')
+    @u_not_mentioned         = create_global_setting_for(create(:user, username: 'regular'), :participating)
+    @u_outsider_mentioned    = create(:user, username: 'outsider')
+
+    # User to be participant by default
+    # This user does not contain any record in notification settings table
+    # It should be treated with a :participating notification_level
+    @u_lazy_participant      = create(:user, username: 'lazy-participant')
 
     create_guest_watcher
 
@@ -665,6 +944,15 @@ describe NotificationService, services: true do
     project.team << [@u_mentioned, :master]
     project.team << [@u_committer, :master]
     project.team << [@u_not_mentioned, :master]
+    project.team << [@u_lazy_participant, :master]
+  end
+
+  def create_global_setting_for(user, level)
+    setting = user.global_notification_setting
+    setting.level = level
+    setting.save
+
+    user
   end
 
   def create_guest_watcher
@@ -677,8 +965,8 @@ describe NotificationService, services: true do
   def add_users_with_subscription(project, issuable)
     @subscriber = create :user
     @unsubscriber = create :user
-    @subscribed_participant = create(:user, username: 'subscribed_participant', notification_level: :participating)
-    @watcher_and_subscriber = create(:user, notification_level: :watch)
+    @subscribed_participant = create_global_setting_for(create(:user, username: 'subscribed_participant'), :participating)
+    @watcher_and_subscriber = create_global_setting_for(create(:user), :watch)
 
     project.team << [@subscribed_participant, :master]
     project.team << [@subscriber, :master]
diff --git a/spec/services/pages_service_spec.rb b/spec/services/pages_service_spec.rb
index e6ad93358a00760e5e6ee851afd1375d57dac95e..ef3c89ac089e2026ef13501d22af0390e8d0dd01 100644
--- a/spec/services/pages_service_spec.rb
+++ b/spec/services/pages_service_spec.rb
@@ -26,7 +26,7 @@ describe PagesService, services: true do
         before { build.status = status }
 
         it 'should not execute worker' do
-          expect(PagesWorker).to_not receive(:perform_async)
+          expect(PagesWorker).not_to receive(:perform_async)
           service.execute
         end
       end
@@ -40,7 +40,7 @@ describe PagesService, services: true do
     end
 
     it 'should not execute worker' do
-      expect(PagesWorker).to_not receive(:perform_async)
+      expect(PagesWorker).not_to receive(:perform_async)
       service.execute
     end
   end
diff --git a/spec/services/path_locks/lock_service_spec.rb b/spec/services/path_locks/lock_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fa1c59660f6279a839761861265dcf36afb2a483
--- /dev/null
+++ b/spec/services/path_locks/lock_service_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe PathLocks::LockService, services: true do
+  let(:current_user) { create(:user) }
+  let(:project)      { create(:empty_project) }
+  let(:path)         { 'app/models' }
+
+  it 'locks path' do
+    allow_any_instance_of(described_class).to receive(:can?).and_return(true)
+    described_class.new(project, current_user).execute(path)
+
+    expect(project.path_locks.find_by(path: path)).to be_truthy
+  end
+
+  it 'raises exception if user has no permissions' do
+    expect do
+      described_class.new(project, current_user).execute(path)
+    end.to raise_exception(PathLocks::LockService::AccessDenied)
+  end
+
+end
diff --git a/spec/services/path_locks/unlock_service_spec.rb b/spec/services/path_locks/unlock_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..069f86dac0faca764b22dbb7f00afded7402dcc3
--- /dev/null
+++ b/spec/services/path_locks/unlock_service_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe PathLocks::UnlockService, services: true do
+  let(:path_lock)    { create :path_lock }
+  let(:current_user) { path_lock.user }
+  let(:project)      { path_lock.project }
+  let(:path)         { path_lock.path }
+
+  it 'unlocks path' do
+    allow_any_instance_of(described_class).to receive(:can?).and_return(true)
+    described_class.new(project, current_user).execute(path_lock)
+
+    expect(project.path_locks.find_by(path: path)).to be_falsey
+  end
+
+  it 'raises exception if user has no permissions' do
+    user = create :user
+
+    expect do
+      described_class.new(project, user).execute(path_lock)
+    end.to raise_exception(PathLocks::UnlockService::AccessDenied)
+  end
+
+end
diff --git a/spec/services/projects/autocomplete_service_spec.rb b/spec/services/projects/autocomplete_service_spec.rb
index 6108c26a78b862808dacd7b9b1d80e6757df8f5f..0971fec2e9f29bf9a34d11c7bcf27e1ba181b6b7 100644
--- a/spec/services/projects/autocomplete_service_spec.rb
+++ b/spec/services/projects/autocomplete_service_spec.rb
@@ -33,6 +33,18 @@ describe Projects::AutocompleteService, services: true do
         expect(issues.count).to eq 1
       end
 
+      it 'should not list project confidential issues for project members with guest role' do
+        project.team << [member, :guest]
+
+        autocomplete = described_class.new(project, non_member)
+        issues = autocomplete.issues.map(&:iid)
+
+        expect(issues).to include issue.iid
+        expect(issues).not_to include security_issue_1.iid
+        expect(issues).not_to include security_issue_2.iid
+        expect(issues.count).to eq 1
+      end
+
       it 'should list project confidential issues for author' do
         autocomplete = described_class.new(project, author)
         issues = autocomplete.issues.map(&:iid)
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index d1ee60a0aeac2a390f106aca0922e80c7634e9dc..31bb7120d84c6732f9eb761014f1454c759f8807 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -42,6 +42,33 @@ describe Projects::ForkService, services: true do
         expect(@to_project.builds_enabled?).to be_truthy
       end
     end
+
+    context "when project has restricted visibility level" do
+      context "and only one visibility level is restricted" do
+        before do
+          @from_project.update_attributes(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+          stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::INTERNAL])
+        end
+
+        it "creates fork with highest allowed level" do
+          forked_project = fork_project(@from_project, @to_user)
+
+          expect(forked_project.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
+        end
+      end
+
+      context "and all visibility levels are restricted" do
+        before do
+          stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC, Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PRIVATE])
+        end
+
+        it "creates fork with private visibility levels" do
+          forked_project = fork_project(@from_project, @to_user)
+
+          expect(forked_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
+        end
+      end
+    end
   end
 
   describe :fork_to_namespace do
diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb
index 7f2dcdab9602d0d9e71b8128df4c77673cd360fb..068c9a1219c984f127524b7004d8843c00cc9975 100644
--- a/spec/services/projects/import_service_spec.rb
+++ b/spec/services/projects/import_service_spec.rb
@@ -49,7 +49,7 @@ describe Projects::ImportService, services: true do
         result = subject.execute
 
         expect(result[:status]).to eq :error
-        expect(result[:message]).to eq 'Failed to import the repository'
+        expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.path_with_namespace} - Failed to import the repository"
       end
     end
 
@@ -124,7 +124,7 @@ describe Projects::ImportService, services: true do
         }
       )
 
-      Gitlab.config.omniauth.providers << provider
+      allow(Gitlab.config.omniauth).to receive(:providers).and_return([provider])
     end
   end
 end
diff --git a/spec/services/projects/update_mirror_service_spec.rb b/spec/services/projects/update_mirror_service_spec.rb
index f42bedd0b3e9af76b1a89242b3d74405499066f8..c19108f009080c2c6b00341ece838e7aa5e6f2ff 100644
--- a/spec/services/projects/update_mirror_service_spec.rb
+++ b/spec/services/projects/update_mirror_service_spec.rb
@@ -1,82 +1,96 @@
 require 'spec_helper'
 
 describe Projects::UpdateMirrorService do
-  let(:project) { create(:project) }
-  let(:repository) { project.repository }
-  let(:mirror_user) { project.owner }
-  subject { described_class.new(project, mirror_user) }
-
-  before do
-    project.import_url = Project::UNKNOWN_IMPORT_URL
-    project.mirror = true
-    project.mirror_user = mirror_user
-    project.save
-  end
+  let(:project) { create(:project, :mirror, import_url: Project::UNKNOWN_IMPORT_URL) }
 
   describe "#execute" do
     it "fetches the upstream repository" do
       expect(project).to receive(:fetch_mirror)
 
-      subject.execute
+      described_class.new(project, project.owner).execute
     end
 
     it "succeeds" do
-      allow(project).to receive(:fetch_mirror) { fetch_mirror(repository) }
+      stub_fetch_mirror(project)
 
-      result = subject.execute
+      result = described_class.new(project, project.owner).execute
 
       expect(result[:status]).to eq(:success)
     end
 
     describe "updating tags" do
       it "creates new tags" do
-        allow(project).to receive(:fetch_mirror) { fetch_mirror(repository) }
+        stub_fetch_mirror(project)
 
-        subject.execute
+        described_class.new(project, project.owner).execute
 
-        expect(repository.tag_names).to include('new-tag')
+        expect(project.repository.tag_names).to include('new-tag')
       end
     end
 
     describe "updating branches" do
       it "creates new branches" do
-        allow(project).to receive(:fetch_mirror) { fetch_mirror(repository) }
+        stub_fetch_mirror(project)
 
-        subject.execute
+        described_class.new(project, project.owner).execute
 
-        expect(repository.branch_names).to include('new-branch')
+        expect(project.repository.branch_names).to include('new-branch')
       end
 
       it "updates existing branches" do
-        allow(project).to receive(:fetch_mirror) { fetch_mirror(repository) }
+        stub_fetch_mirror(project)
 
-        subject.execute
+        described_class.new(project, project.owner).execute
 
-        expect(repository.find_branch('existing-branch').target).to eq(repository.find_branch('master').target)
+        expect(project.repository.find_branch('existing-branch').target)
+          .to eq(project.repository.find_branch('master').target)
       end
 
       it "doesn't update diverged branches" do
-        allow(project).to receive(:fetch_mirror) { fetch_mirror(repository) }
+        stub_fetch_mirror(project)
 
-        subject.execute
+        described_class.new(project, project.owner).execute
 
-        expect(repository.find_branch('markdown').target).not_to eq(repository.find_branch('master').target)
+        expect(project.repository.find_branch('markdown').target)
+          .not_to eq(project.repository.find_branch('master').target)
       end
     end
 
     describe "when the mirror user doesn't have access" do
-      let(:mirror_user) { create(:user) }
+      it "fails" do
+        stub_fetch_mirror(project)
+
+        result = described_class.new(project, build_stubbed(:user)).execute
+
+        expect(result[:status]).to eq(:error)
+      end
+    end
+
+    describe "when no user is present" do
+      it "fails" do
+        result = described_class.new(project, nil).execute
+
+        expect(result[:status]).to eq(:error)
+      end
+    end
+
+    describe "when is no mirror" do
+      let(:project) { build_stubbed(:project) }
 
       it "fails" do
-        allow(project).to receive(:fetch_mirror) { fetch_mirror(repository) }
+        expect(project.mirror?).to eq(false)
 
-        result = subject.execute
+        result = described_class.new(project, build_stubbed(:user)).execute
 
         expect(result[:status]).to eq(:error)
       end
     end
   end
 
+  def stub_fetch_mirror(project, repository: project.repository)
+    allow(project).to receive(:fetch_mirror) { fetch_mirror(repository) }
+  end
+
   def fetch_mirror(repository)
     rugged = repository.rugged
     masterrev = repository.find_branch('master').target
diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb
index 4d6a604a2b2eaf403f188bc73b2dc572fe2eda55..411b22a0fb83d5d198b71c05e3014fd6efdf64fa 100644
--- a/spec/services/projects/update_pages_service_spec.rb
+++ b/spec/services/projects/update_pages_service_spec.rb
@@ -2,8 +2,8 @@ require "spec_helper"
 
 describe Projects::UpdatePagesService do
   let(:project) { create :project }
-  let(:commit) { create :ci_commit, project: project, sha: project.commit('HEAD').sha }
-  let(:build) { create :ci_build, commit: commit, ref: 'HEAD' }
+  let(:pipeline) { create :ci_pipeline, project: project, sha: project.commit('HEAD').sha }
+  let(:build) { create :ci_build, pipeline: pipeline, ref: 'HEAD' }
   let(:invalid_file) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png') }
 
   subject { described_class.new(project, build) }
@@ -34,7 +34,7 @@ describe Projects::UpdatePagesService do
 
       it 'limits pages size' do
         stub_application_setting(max_pages_size: 1)
-        expect(execute).to_not eq(:success)
+        expect(execute).not_to eq(:success)
       end
 
       it 'removes pages after destroy' do
@@ -47,31 +47,31 @@ describe Projects::UpdatePagesService do
       end
 
       it 'fails if sha on branch is not latest' do
-        commit.update_attributes(sha: 'old_sha')
+        pipeline.update_attributes(sha: 'old_sha')
         build.update_attributes(artifacts_file: file)
-        expect(execute).to_not eq(:success)
+        expect(execute).not_to eq(:success)
       end
 
       it 'fails for empty file fails' do
         build.update_attributes(artifacts_file: empty_file)
-        expect(execute).to_not eq(:success)
+        expect(execute).not_to eq(:success)
       end
     end
   end
 
   it 'fails to remove project pages when no pages is deployed' do
-    expect(PagesWorker).to_not receive(:perform_in)
+    expect(PagesWorker).not_to receive(:perform_in)
     expect(project.pages_deployed?).to be_falsey
     project.destroy
   end
 
   it 'fails if no artifacts' do
-    expect(execute).to_not eq(:success)
+    expect(execute).not_to eq(:success)
   end
 
   it 'fails for invalid archive' do
     build.update_attributes(artifacts_file: invalid_file)
-    expect(execute).to_not eq(:success)
+    expect(execute).not_to eq(:success)
   end
 
   def execute
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index d65b408bfd073d82921289ed58ca20eb4c45e830..4c6f6bc37cbd45d01db2fc1c7b18bf6932195ce3 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -208,8 +208,10 @@ describe SystemNoteService, services: true do
   end
 
   describe '.merge_when_build_succeeds' do
-    let(:ci_commit) { build :ci_commit_without_jobs }
-    let(:noteable) { create :merge_request }
+    let(:pipeline) { build(:ci_pipeline_without_jobs )}
+    let(:noteable) do
+      create(:merge_request, source_project: project, target_project: project)
+    end
 
     subject { described_class.merge_when_build_succeeds(noteable, project, author, noteable.last_commit) }
 
@@ -221,8 +223,9 @@ describe SystemNoteService, services: true do
   end
 
   describe '.cancel_merge_when_build_succeeds' do
-    let(:ci_commit) { build :ci_commit_without_jobs }
-    let(:noteable) { create :merge_request }
+    let(:noteable) do
+      create(:merge_request, source_project: project, target_project: project)
+    end
 
     subject { described_class.cancel_merge_when_build_succeeds(noteable, project, author) }
 
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index 421477365323b7ea5fcd1db1d2b852d6100b6e51..26f09cdbaf9e4995d2705a4510ebf45269bf0357 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -5,20 +5,22 @@ describe TodoService, services: true do
   let(:assignee) { create(:user) }
   let(:non_member) { create(:user) }
   let(:member) { create(:user) }
+  let(:guest) { create(:user) }
   let(:admin) { create(:admin) }
   let(:john_doe) { create(:user) }
   let(:project) { create(:project) }
-  let(:mentions) { [author, assignee, john_doe, member, non_member, admin].map(&:to_reference).join(' ') }
+  let(:mentions) { [author, assignee, john_doe, member, guest, non_member, admin].map(&:to_reference).join(' ') }
   let(:service) { described_class.new }
 
   before do
+    project.team << [guest, :guest]
     project.team << [author, :developer]
     project.team << [member, :developer]
     project.team << [john_doe, :developer]
   end
 
   describe 'Issues' do
-    let(:issue) { create(:issue, project: project, assignee: john_doe, author: author, description: mentions) }
+    let(:issue) { create(:issue, project: project, assignee: john_doe, author: author, description: "- [ ] Task 1\n- [ ] Task 2 #{mentions}") }
     let(:unassigned_issue) { create(:issue, project: project, assignee: nil) }
     let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee, description: mentions) }
 
@@ -41,18 +43,20 @@ describe TodoService, services: true do
         service.new_issue(issue, author)
 
         should_create_todo(user: member, target: issue, action: Todo::MENTIONED)
+        should_create_todo(user: guest, target: issue, action: Todo::MENTIONED)
         should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED)
         should_not_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED)
         should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
       end
 
-      it 'does not create todo for non project members when issue is confidential' do
+      it 'does not create todo if user can not see the issue when issue is confidential' do
         service.new_issue(confidential_issue, john_doe)
 
         should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::ASSIGNED)
         should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
         should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
         should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
+        should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
         should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
       end
 
@@ -81,6 +85,7 @@ describe TodoService, services: true do
         service.update_issue(issue, author)
 
         should_create_todo(user: member, target: issue, action: Todo::MENTIONED)
+        should_create_todo(user: guest, target: issue, action: Todo::MENTIONED)
         should_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED)
         should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED)
         should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
@@ -92,15 +97,29 @@ describe TodoService, services: true do
         expect { service.update_issue(issue, author) }.not_to change(member.todos, :count)
       end
 
-      it 'does not create todo for non project members when issue is confidential' do
+      it 'does not create todo if user can not see the issue when issue is confidential' do
         service.update_issue(confidential_issue, john_doe)
 
         should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
         should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
         should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
         should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
+        should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
         should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
       end
+
+      it 'does not create todo when when tasks are marked as completed' do
+        issue.update(description: "- [x] Task 1\n- [X] Task 2 #{mentions}")
+
+        service.update_issue(issue, author)
+
+        should_not_create_todo(user: admin, target: issue, action: Todo::MENTIONED)
+        should_not_create_todo(user: assignee, target: issue, action: Todo::MENTIONED)
+        should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED)
+        should_not_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED)
+        should_not_create_todo(user: member, target: issue, action: Todo::MENTIONED)
+        should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
+      end
     end
 
     describe '#close_issue' do
@@ -156,7 +175,6 @@ describe TodoService, services: true do
       let(:note_on_commit) { create(:note_on_commit, project: project, author: john_doe, note: mentions) }
       let(:note_on_confidential_issue) { create(:note_on_issue, noteable: confidential_issue, project: project, note: mentions) }
       let(:note_on_project_snippet) { create(:note_on_project_snippet, project: project, author: john_doe, note: mentions) }
-      let(:award_note) { create(:note, :award, project: project, noteable: issue, author: john_doe, note: 'thumbsup') }
       let(:system_note) { create(:system_note, project: project, noteable: issue) }
 
       it 'mark related pending todos to the noteable for the note author as done' do
@@ -169,13 +187,6 @@ describe TodoService, services: true do
         expect(second_todo.reload).to be_done
       end
 
-      it 'mark related pending todos to the noteable for the award note author as done' do
-        service.new_note(award_note, john_doe)
-
-        expect(first_todo.reload).to be_done
-        expect(second_todo.reload).to be_done
-      end
-
       it 'does not mark related pending todos it is a system note' do
         service.new_note(system_note, john_doe)
 
@@ -187,18 +198,20 @@ describe TodoService, services: true do
         service.new_note(note, john_doe)
 
         should_create_todo(user: member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
+        should_create_todo(user: guest, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
         should_create_todo(user: author, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
         should_not_create_todo(user: john_doe, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
         should_not_create_todo(user: non_member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
       end
 
-      it 'does not create todo for non project members when leaving a note on a confidential issue' do
+      it 'does not create todo if user can not see the issue when leaving a note on a confidential issue' do
         service.new_note(note_on_confidential_issue, john_doe)
 
         should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
         should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
         should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
         should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
+        should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
         should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
       end
 
@@ -215,10 +228,18 @@ describe TodoService, services: true do
         should_not_create_any_todo { service.new_note(note_on_project_snippet, john_doe) }
       end
     end
+
+    describe '#mark_todo' do
+      it 'creates a todo from a issue' do
+        service.mark_todo(unassigned_issue, author)
+
+        should_create_todo(user: author, target: unassigned_issue, action: Todo::MARKED)
+      end
+    end
   end
 
   describe 'Merge Requests' do
-    let(:mr_assigned) { create(:merge_request, source_project: project, author: author, assignee: john_doe, description: mentions) }
+    let(:mr_assigned) { create(:merge_request, source_project: project, author: author, assignee: john_doe, description: "- [ ] Task 1\n- [ ] Task 2 #{mentions}") }
     let(:mr_unassigned) { create(:merge_request, source_project: project, author: author, assignee: nil) }
 
     describe '#new_merge_request' do
@@ -240,6 +261,7 @@ describe TodoService, services: true do
         service.new_merge_request(mr_assigned, author)
 
         should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
+        should_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
         should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
         should_not_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
         should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
@@ -251,6 +273,7 @@ describe TodoService, services: true do
         service.update_merge_request(mr_assigned, author)
 
         should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
+        should_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
         should_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
         should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
         should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
@@ -261,6 +284,19 @@ describe TodoService, services: true do
 
         expect { service.update_merge_request(mr_assigned, author) }.not_to change(member.todos, :count)
       end
+
+      it 'does not create todo when when tasks are marked as completed' do
+        mr_assigned.update(description: "- [x] Task 1\n- [X] Task 2 #{mentions}")
+
+        service.update_merge_request(mr_assigned, author)
+
+        should_not_create_todo(user: admin, target: mr_assigned, action: Todo::MENTIONED)
+        should_not_create_todo(user: assignee, target: mr_assigned, action: Todo::MENTIONED)
+        should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
+        should_not_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
+        should_not_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
+        should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
+      end
     end
 
     describe '#close_merge_request' do
@@ -306,6 +342,15 @@ describe TodoService, services: true do
       end
     end
 
+    describe '#new_award_emoji' do
+      it 'marks related pending todos to the target for the user as done' do
+        todo = create(:todo, user: john_doe, project: project, target: mr_assigned, author: author)
+        service.new_award_emoji(mr_assigned, john_doe)
+
+        expect(todo.reload).to be_done
+      end
+    end
+
     describe '#merge_request_build_failed' do
       it 'creates a pending todo for the merge request author' do
         service.merge_request_build_failed(mr_unassigned)
@@ -324,6 +369,14 @@ describe TodoService, services: true do
         expect(second_todo.reload).not_to be_done
       end
     end
+
+    describe '#mark_todo' do
+      it 'creates a todo from a merge request' do
+        service.mark_todo(mr_unassigned, author)
+
+        should_create_todo(user: author, target: mr_unassigned, action: Todo::MARKED)
+      end
+    end
   end
 
   def should_create_todo(attributes = {})
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index dd628d5dbb190f7640b79f59aacdfa6b63963d81..01c5713ab486e988ed4dc272f96a2e667a121d38 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -16,6 +16,11 @@ require 'shoulda/matchers'
 require 'sidekiq/testing/inline'
 require 'rspec/retry'
 
+if ENV['CI']
+  require 'knapsack'
+  Knapsack::Adapters::RSpecAdapter.bind
+end
+
 # Requires supporting ruby files with custom matchers and macros, etc,
 # in spec/support/ and its subdirectories.
 Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
diff --git a/spec/support/fake_u2f_device.rb b/spec/support/fake_u2f_device.rb
new file mode 100644
index 0000000000000000000000000000000000000000..553fe9f1fbc8e163754330de4e816a0cc6dd5663
--- /dev/null
+++ b/spec/support/fake_u2f_device.rb
@@ -0,0 +1,36 @@
+class FakeU2fDevice
+  def initialize(page)
+    @page = page
+  end
+  
+  def respond_to_u2f_registration
+    app_id = @page.evaluate_script('gon.u2f.app_id')
+    challenges = @page.evaluate_script('gon.u2f.challenges')
+
+    json_response = u2f_device(app_id).register_response(challenges[0])
+
+    @page.execute_script("
+    u2f.register = function(appId, registerRequests, signRequests, callback) {
+      callback(#{json_response});
+    };
+    ")
+  end
+
+  def respond_to_u2f_authentication
+    app_id = @page.evaluate_script('gon.u2f.app_id')
+    challenges = @page.evaluate_script('gon.u2f.challenges')
+    json_response = u2f_device(app_id).sign_response(challenges[0])
+
+    @page.execute_script("
+    u2f.sign = function(appId, challenges, signRequests, callback) {
+      callback(#{json_response});
+    };
+    ")
+  end
+
+  private
+
+  def u2f_device(app_id)
+    @u2f_device ||= U2F::FakeU2F.new(app_id)
+  end
+end
diff --git a/spec/support/filter_spec_helper.rb b/spec/support/filter_spec_helper.rb
index e849a9633b9edea577304c99fd735251e075a23e..a8e454eb09efebc7e693d2db4ad6f832ad2e2b4f 100644
--- a/spec/support/filter_spec_helper.rb
+++ b/spec/support/filter_spec_helper.rb
@@ -40,8 +40,7 @@ module FilterSpecHelper
 
     filters = [
       Banzai::Filter::AutolinkFilter,
-      described_class,
-      Banzai::Filter::ReferenceGathererFilter
+      described_class
     ]
 
     HTML::Pipeline.new(filters, context)
diff --git a/spec/controllers/import/import_spec_helper.rb b/spec/support/import_spec_helper.rb
similarity index 90%
rename from spec/controllers/import/import_spec_helper.rb
rename to spec/support/import_spec_helper.rb
index 9d7648e25a71b7036b34359c218f9dd0aeb6bebe..6710962f0822e916ad8c5458673cfe56155eba25 100644
--- a/spec/controllers/import/import_spec_helper.rb
+++ b/spec/support/import_spec_helper.rb
@@ -28,6 +28,6 @@ module ImportSpecHelper
       app_id: 'asd123',
       app_secret: 'asd123'
     )
-    Gitlab.config.omniauth.providers << provider
+    allow(Gitlab.config.omniauth).to receive(:providers).and_return([provider])
   end
 end
diff --git a/spec/support/login_helpers.rb b/spec/support/login_helpers.rb
index 6d658e2b90b1ad2b3d0e83c7e7a19d9c056a639b..5be63d981b889fe21f5e28e719620b4e07d45075 100644
--- a/spec/support/login_helpers.rb
+++ b/spec/support/login_helpers.rb
@@ -26,11 +26,13 @@ module LoginHelpers
 
   # Internal: Login as the specified user
   #
-  # user - User instance to login with
-  def login_with(user)
+  # user     - User instance to login with
+  # remember - Whether or not to check "Remember me" (default: false)
+  def login_with(user, remember: false)
     visit new_user_session_path
     fill_in "user_login", with: user.email
     fill_in "user_password", with: "12345678"
+    check 'user_remember_me' if remember
     click_button "Sign in"
     Thread.current[:current_user] = user
   end
diff --git a/spec/support/markdown_feature.rb b/spec/support/markdown_feature.rb
index 7fc6d6fcc5ea1f286189be2ac17f3f50d112973f..a79386b5db9618dc1eeb52ee016ad124fa39c3a5 100644
--- a/spec/support/markdown_feature.rb
+++ b/spec/support/markdown_feature.rb
@@ -32,6 +32,10 @@ class MarkdownFeature
     @project_wiki ||= ProjectWiki.new(project, user)
   end
 
+  def project_wiki_page
+    @project_wiki_page ||= build(:wiki_page, wiki: project_wiki)
+  end
+
   def issue
     @issue ||= create(:issue, project: project)
   end
diff --git a/spec/support/reference_parser_helpers.rb b/spec/support/reference_parser_helpers.rb
new file mode 100644
index 0000000000000000000000000000000000000000..01689194eac2b7b5d2f2caf5530d06edda2a2047
--- /dev/null
+++ b/spec/support/reference_parser_helpers.rb
@@ -0,0 +1,5 @@
+module ReferenceParserHelpers
+  def empty_html_link
+    Nokogiri::HTML.fragment('<a></a>').children[0]
+  end
+end
diff --git a/spec/support/stub_gitlab_calls.rb b/spec/support/stub_gitlab_calls.rb
index f73416a3d0fc8285b86f8dff30e78a91678a29f4..93f96cacc00178f72768284f6d99fdb577708a8e 100644
--- a/spec/support/stub_gitlab_calls.rb
+++ b/spec/support/stub_gitlab_calls.rb
@@ -13,12 +13,12 @@ module StubGitlabCalls
     allow_any_instance_of(Network).to receive(:projects) { project_hash_array }
   end
 
-  def stub_ci_commit_to_return_yaml_file
-    stub_ci_commit_yaml_file(gitlab_ci_yaml)
+  def stub_ci_pipeline_to_return_yaml_file
+    stub_ci_pipeline_yaml_file(gitlab_ci_yaml)
   end
 
-  def stub_ci_commit_yaml_file(ci_yaml)
-    allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file) { ci_yaml }
+  def stub_ci_pipeline_yaml_file(ci_yaml)
+    allow_any_instance_of(Ci::Pipeline).to receive(:ci_yaml_file) { ci_yaml }
   end
 
   def stub_ci_builds_disabled
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index 71664bb192ed5fe33b379b0d49989999b41adabc..498bd4bf8000e9203c643f93aa129d6e4f225f70 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -16,6 +16,7 @@ module TestEnv
     'master'           => '5937ac0',
     "'test'"           => 'e56497b',
     'orphaned-branch'  => '45127a9',
+    'binary-encoding'  => '7b1cf43',
   }
 
   # gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index fa33c805e01cba30473e5362d9dc3aaf8ea19112..3cb62dfa10f9baa37382e155626f07315a2b0450 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -2,6 +2,8 @@ require 'spec_helper'
 require 'rake'
 
 describe 'gitlab:app namespace rake task' do
+  let(:enable_registry) { true }
+
   before :all do
     Rake.application.rake_require 'tasks/gitlab/task_helpers'
     Rake.application.rake_require 'tasks/gitlab/backup'
@@ -15,6 +17,10 @@ describe 'gitlab:app namespace rake task' do
     FileUtils.mkdir_p('public/uploads')
   end
 
+  before do
+    stub_container_registry_config(enabled: enable_registry)
+  end
+
   def run_rake_task(task_name)
     Rake::Task[task_name].reenable
     Rake.application.invoke_task task_name
@@ -145,6 +151,18 @@ describe 'gitlab:app namespace rake task' do
 
       expect(temp_dirs).to be_empty
     end
+
+    context 'registry disabled' do
+      let(:enable_registry) { false }
+
+      it 'should not create registry.tar.gz' do
+        tar_contents, exit_status = Gitlab::Popen.popen(
+          %W{tar -tvf #{@backup_tar}}
+        )
+        expect(exit_status).to eq(0)
+        expect(tar_contents).not_to match('registry.tar.gz')
+      end
+    end
   end # backup_create task
 
   describe "Skipping items" do
diff --git a/spec/tasks/gitlab/db_rake_spec.rb b/spec/tasks/gitlab/db_rake_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..36d03a224e4f212fcd88b2e40c19763a2f368735
--- /dev/null
+++ b/spec/tasks/gitlab/db_rake_spec.rb
@@ -0,0 +1,62 @@
+require 'spec_helper'
+require 'rake'
+
+describe 'gitlab:db namespace rake task' do
+  before :all do
+    Rake.application.rake_require 'active_record/railties/databases'
+    Rake.application.rake_require 'tasks/seed_fu'
+    Rake.application.rake_require 'tasks/gitlab/db'
+
+    # empty task as env is already loaded
+    Rake::Task.define_task :environment
+  end
+
+  before do
+    # Stub out db tasks
+    allow(Rake::Task['db:migrate']).to receive(:invoke).and_return(true)
+    allow(Rake::Task['db:schema:load']).to receive(:invoke).and_return(true)
+    allow(Rake::Task['db:seed_fu']).to receive(:invoke).and_return(true)
+  end
+
+  describe 'configure' do
+    it 'should invoke db:migrate when schema has already been loaded' do
+      allow(ActiveRecord::Base.connection).to receive(:tables).and_return(['default'])
+      expect(Rake::Task['db:migrate']).to receive(:invoke)
+      expect(Rake::Task['db:schema:load']).not_to receive(:invoke)
+      expect(Rake::Task['db:seed_fu']).not_to receive(:invoke)
+      expect { run_rake_task('gitlab:db:configure') }.not_to raise_error
+    end
+
+    it 'should invoke db:shema:load and db:seed_fu when schema is not loaded' do
+      allow(ActiveRecord::Base.connection).to receive(:tables).and_return([])
+      expect(Rake::Task['db:schema:load']).to receive(:invoke)
+      expect(Rake::Task['db:seed_fu']).to receive(:invoke)
+      expect(Rake::Task['db:migrate']).not_to receive(:invoke)
+      expect { run_rake_task('gitlab:db:configure') }.not_to raise_error
+    end
+
+    it 'should not invoke any other rake tasks during an error' do
+      allow(ActiveRecord::Base).to receive(:connection).and_raise(RuntimeError, 'error')
+      expect(Rake::Task['db:migrate']).not_to receive(:invoke)
+      expect(Rake::Task['db:schema:load']).not_to receive(:invoke)
+      expect(Rake::Task['db:seed_fu']).not_to receive(:invoke)
+      expect { run_rake_task('gitlab:db:configure') }.to raise_error(RuntimeError, 'error')
+      # unstub connection so that the database cleaner still works
+      allow(ActiveRecord::Base).to receive(:connection).and_call_original
+    end
+
+    it 'should not invoke seed after a failed schema_load' do
+      allow(ActiveRecord::Base.connection).to receive(:tables).and_return([])
+      allow(Rake::Task['db:schema:load']).to receive(:invoke).and_raise(RuntimeError, 'error')
+      expect(Rake::Task['db:schema:load']).to receive(:invoke)
+      expect(Rake::Task['db:seed_fu']).not_to receive(:invoke)
+      expect(Rake::Task['db:migrate']).not_to receive(:invoke)
+      expect { run_rake_task('gitlab:db:configure') }.to raise_error(RuntimeError, 'error')
+    end
+  end
+
+  def run_rake_task(task_name)
+    Rake::Task[task_name].reenable
+    Rake.application.invoke_task task_name
+  end
+end
diff --git a/spec/teaspoon_env.rb b/spec/teaspoon_env.rb
index 58f45ff86102a8fe43f8d03cd0a32f82a2ab0cf2..69b2b9b6d5bf58ff72d1e4ebe177ec0a6f757cc6 100644
--- a/spec/teaspoon_env.rb
+++ b/spec/teaspoon_env.rb
@@ -41,11 +41,11 @@ Teaspoon.configure do |config|
     suite.matcher = "{spec/javascripts,app/assets}/**/*_spec.{js,js.coffee,coffee}"
 
     # Load additional JS files, but requiring them in your spec helper is the preferred way to do this.
-    #suite.javascripts = []
+    # suite.javascripts = []
 
     # You can include your own stylesheets if you want to change how Teaspoon looks.
     # Note: Spec related CSS can and should be loaded using fixtures.
-    #suite.stylesheets = ["teaspoon"]
+    # suite.stylesheets = ["teaspoon"]
 
     # This suites spec helper, which can require additional support files. This file is loaded before any of your test
     # files are loaded.
@@ -62,19 +62,19 @@ Teaspoon.configure do |config|
 
     # Hooks allow you to use `Teaspoon.hook("fixtures")` before, after, or during your spec run. This will make a
     # synchronous Ajax request to the server that will call all of the blocks you've defined for that hook name.
-    #suite.hook :fixtures, &proc{}
+    # suite.hook :fixtures, &proc{}
 
     # Determine whether specs loaded into the test harness should be embedded as individual script tags or concatenated
-    # into a single file. Similar to Rails' asset `debug: true` and `config.assets.debug = true` options. By default, 
+    # into a single file. Similar to Rails' asset `debug: true` and `config.assets.debug = true` options. By default,
     # Teaspoon expands all assets to provide more valuable stack traces that reference individual source files.
-    #suite.expand_assets = true
+    # suite.expand_assets = true
   end
 
   # Example suite. Since we're just filtering to files already within the root test/javascripts, these files will also
   # be run in the default suite -- but can be focused into a more specific suite.
-  #config.suite :targeted do |suite|
+  # config.suite :targeted do |suite|
   #  suite.matcher = "spec/javascripts/targeted/*_spec.{js,js.coffee,coffee}"
-  #end
+  # end
 
   # CONSOLE RUNNER SPECIFIC
   #
@@ -94,45 +94,45 @@ Teaspoon.configure do |config|
   # PhantomJS: https://github.com/modeset/teaspoon/wiki/Using-PhantomJS
   # Selenium Webdriver: https://github.com/modeset/teaspoon/wiki/Using-Selenium-WebDriver
   # Capybara Webkit: https://github.com/modeset/teaspoon/wiki/Using-Capybara-Webkit
-  #config.driver = :phantomjs
+  # config.driver = :phantomjs
 
   # Specify additional options for the driver.
   #
   # PhantomJS: https://github.com/modeset/teaspoon/wiki/Using-PhantomJS
   # Selenium Webdriver: https://github.com/modeset/teaspoon/wiki/Using-Selenium-WebDriver
   # Capybara Webkit: https://github.com/modeset/teaspoon/wiki/Using-Capybara-Webkit
-  #config.driver_options = nil
+  # config.driver_options = nil
 
   # Specify the timeout for the driver. Specs are expected to complete within this time frame or the run will be
   # considered a failure. This is to avoid issues that can arise where tests stall.
-  #config.driver_timeout = 180
+  # config.driver_timeout = 180
 
   # Specify a server to use with Rack (e.g. thin, mongrel). If nil is provided Rack::Server is used.
-  #config.server = nil
+  # config.server = nil
 
   # Specify a port to run on a specific port, otherwise Teaspoon will use a random available port.
-  #config.server_port = nil
+  # config.server_port = nil
 
   # Timeout for starting the server in seconds. If your server is slow to start you may have to bump this, or you may
   # want to lower this if you know it shouldn't take long to start.
-  #config.server_timeout = 20
+  # config.server_timeout = 20
 
   # Force Teaspoon to fail immediately after a failing suite. Can be useful to make Teaspoon fail early if you have
   # several suites, but in environments like CI this may not be desirable.
-  #config.fail_fast = true
+  # config.fail_fast = true
 
   # Specify the formatters to use when outputting the results.
   # Note: Output files can be specified by using `"junit>/path/to/output.xml"`.
   #
   # Available: :dot, :clean, :documentation, :json, :junit, :pride, :rspec_html, :snowday, :swayze_or_oprah, :tap, :tap_y, :teamcity
-  #config.formatters = [:dot]
+  # config.formatters = [:dot]
 
   # Specify if you want color output from the formatters.
-  #config.color = true
+  # config.color = true
 
   # Teaspoon pipes all console[log/debug/error] to $stdout. This is useful to catch places where you've forgotten to
   # remove them, but in verbose applications this may not be desirable.
-  #config.suppress_log = false
+  # config.suppress_log = false
 
   # COVERAGE REPORTS / THRESHOLD ASSERTIONS
   #
@@ -149,7 +149,7 @@ Teaspoon.configure do |config|
   # Specify that you always want a coverage configuration to be used. Otherwise, specify that you want coverage
   # on the CLI.
   # Set this to "true" or the name of your coverage config.
-  #config.use_coverage = nil
+  # config.use_coverage = nil
 
   # You can have multiple coverage configs by passing a name to config.coverage.
   # e.g. config.coverage :ci do |coverage|
@@ -158,21 +158,21 @@ Teaspoon.configure do |config|
     # Which coverage reports Istanbul should generate. Correlates directly to what Istanbul supports.
     #
     # Available: text-summary, text, html, lcov, lcovonly, cobertura, teamcity
-    #coverage.reports = ["text-summary", "html"]
+    # coverage.reports = ["text-summary", "html"]
 
     # The path that the coverage should be written to - when there's an artifact to write to disk.
     # Note: Relative to `config.root`.
-    #coverage.output_path = "coverage"
+    # coverage.output_path = "coverage"
 
     # Assets to be ignored when generating coverage reports. Accepts an array of filenames or regular expressions. The
     # default excludes assets from vendor, gems and support libraries.
-    #coverage.ignore = [%r{/lib/ruby/gems/}, %r{/vendor/assets/}, %r{/support/}, %r{/(.+)_helper.}]
+    # coverage.ignore = [%r{/lib/ruby/gems/}, %r{/vendor/assets/}, %r{/support/}, %r{/(.+)_helper.}]
 
     # Various thresholds requirements can be defined, and those thresholds will be checked at the end of a run. If any
     # aren't met the run will fail with a message. Thresholds can be defined as a percentage (0-100), or nil.
-    #coverage.statements = nil
-    #coverage.functions = nil
-    #coverage.branches = nil
-    #coverage.lines = nil
+    # coverage.statements = nil
+    # coverage.functions = nil
+    # coverage.branches = nil
+    # coverage.lines = nil
   end
 end
diff --git a/spec/views/admin/users/index.html.haml_spec.rb b/spec/views/admin/users/index.html.haml_spec.rb
index ffa81723856aec24aba67180eb8ae31ecdd6c718..72cdf19141266b55aaf6af545b957c27f5e40e70 100644
--- a/spec/views/admin/users/index.html.haml_spec.rb
+++ b/spec/views/admin/users/index.html.haml_spec.rb
@@ -2,6 +2,7 @@ require 'rails_helper'
 
 describe 'admin/users/index' do
   it 'includes "Send email to users" link' do
+    allow(view).to receive(:container_class).and_return('ignored')
     assign(:users, User.all.page(1))
 
     render
diff --git a/spec/workers/expire_build_artifacts_worker_spec.rb b/spec/workers/expire_build_artifacts_worker_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e3827cae9a611e652d9a0e754113352fbd02547c
--- /dev/null
+++ b/spec/workers/expire_build_artifacts_worker_spec.rb
@@ -0,0 +1,57 @@
+require 'spec_helper'
+
+describe ExpireBuildArtifactsWorker do
+  include RepoHelpers
+
+  let(:worker) { described_class.new }
+
+  describe '#perform' do
+    before { build }
+
+    subject! { worker.perform }
+
+    context 'with expired artifacts' do
+      let(:build) { create(:ci_build, :artifacts, artifacts_expire_at: Time.now - 7.days) }
+
+      it 'does expire' do
+        expect(build.reload.artifacts_expired?).to be_truthy
+      end
+
+      it 'does remove files' do
+        expect(build.reload.artifacts_file.exists?).to be_falsey
+      end
+    end
+
+    context 'with not yet expired artifacts' do
+      let(:build) { create(:ci_build, :artifacts, artifacts_expire_at: Time.now + 7.days) }
+
+      it 'does not expire' do
+        expect(build.reload.artifacts_expired?).to be_falsey
+      end
+
+      it 'does not remove files' do
+        expect(build.reload.artifacts_file.exists?).to be_truthy
+      end
+    end
+
+    context 'without expire date' do
+      let(:build) { create(:ci_build, :artifacts) }
+
+      it 'does not expire' do
+        expect(build.reload.artifacts_expired?).to be_falsey
+      end
+
+      it 'does not remove files' do
+        expect(build.reload.artifacts_file.exists?).to be_truthy
+      end
+    end
+
+    context 'for expired artifacts' do
+      let(:build) { create(:ci_build, artifacts_expire_at: Time.now - 7.days) }
+
+      it 'is still expired' do
+        expect(build.reload.artifacts_expired?).to be_truthy
+      end
+    end
+  end
+end
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index e56ac32ecc1b98ed44ee364efa764b4132a41999..1d6ea5f26cb7237249f019c12169e55ff2770ddd 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -52,16 +52,16 @@ describe PostReceive do
     context "gitlab-ci.yml" do
       subject { PostReceive.new.perform(pwd(project), key_id, base64_changes) }
 
-      context "creates a Ci::Commit for every change" do
-        before { stub_ci_commit_to_return_yaml_file }
+      context "creates a Ci::Pipeline for every change" do
+        before { stub_ci_pipeline_to_return_yaml_file }
 
-        it { expect{ subject }.to change{ Ci::Commit.count }.by(2) }
+        it { expect{ subject }.to change{ Ci::Pipeline.count }.by(2) }
       end
 
-      context "does not create a Ci::Commit" do
-        before { stub_ci_commit_yaml_file(nil) }
+      context "does not create a Ci::Pipeline" do
+        before { stub_ci_pipeline_yaml_file(nil) }
 
-        it { expect{ subject }.to_not change{ Ci::Commit.count } }
+        it { expect{ subject }.not_to change{ Ci::Pipeline.count } }
       end
     end
   end
@@ -75,7 +75,7 @@ describe PostReceive do
     it "triggers wiki index update" do
       expect(Project).to receive(:find_with_namespace).with("#{project.path_with_namespace}.wiki").and_return(nil)
       expect(Project).to receive(:find_with_namespace).with(project.path_with_namespace).and_return(project)
-      allow(Gitlab.config.elasticsearch).to receive(:enabled).and_return(true)
+      stub_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
       expect_any_instance_of(ProjectWiki).to receive(:index_blobs)
 
       repo_path = "#{pwd(project)}.wiki"
diff --git a/spec/workers/repository_update_mirror_worker_spec.rb b/spec/workers/repository_update_mirror_worker_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..29f8b8ef9b99b3a04458f863830c010767757cac
--- /dev/null
+++ b/spec/workers/repository_update_mirror_worker_spec.rb
@@ -0,0 +1,44 @@
+require 'rails_helper'
+
+describe RepositoryUpdateMirrorWorker do
+  describe '#perform' do
+    it "just returns if cannot obtain a lease" do
+      worker = described_class.new
+      project_id = 15
+
+      allow_any_instance_of(Gitlab::ExclusiveLease)
+        .to receive(:try_obtain).and_return(false)
+
+      expect(Projects::UpdateMirrorService).not_to receive(:execute)
+
+      worker.perform(project_id)
+    end
+
+    context "when obtain the lease" do
+      before do
+        allow_any_instance_of(Gitlab::ExclusiveLease)
+          .to receive(:try_obtain).and_return(true)
+      end
+
+      it "sets import as finished when update mirror service executes successfully" do
+        project = create(:empty_project, :mirror)
+
+        expect_any_instance_of(Projects::UpdateMirrorService).to receive(:execute).and_return(status: :success)
+
+        expect do
+          described_class.new.perform(project.id)
+        end.to change { project.reload.import_status }.to("finished")
+      end
+
+      it "sets import as failed when update mirror service executes with errors" do
+        project = create(:empty_project, :mirror)
+
+        expect_any_instance_of(Projects::UpdateMirrorService).to receive(:execute).and_return(status: :error, message: 'fail!')
+
+        expect do
+          described_class.new.perform(project.id)
+        end.to change { project.reload.import_status }.to("failed")
+      end
+    end
+  end
+end
diff --git a/spec/workers/stuck_ci_builds_worker_spec.rb b/spec/workers/stuck_ci_builds_worker_spec.rb
index 665ec20f2243a7f5cc68fa72bd244d341bad8ccd..801fa31b45d8db39becde154f01cf1d2b86fd4e9 100644
--- a/spec/workers/stuck_ci_builds_worker_spec.rb
+++ b/spec/workers/stuck_ci_builds_worker_spec.rb
@@ -2,6 +2,7 @@ require "spec_helper"
 
 describe StuckCiBuildsWorker do
   let!(:build) { create :ci_build }
+  let(:worker) { described_class.new }
 
   subject do
     build.reload
@@ -16,13 +17,13 @@ describe StuckCiBuildsWorker do
 
       it 'gets dropped if it was updated over 2 days ago' do
         build.update!(updated_at: 2.days.ago)
-        StuckCiBuildsWorker.new.perform
+        worker.perform
         is_expected.to eq('failed')
       end
 
       it "is still #{status}" do
         build.update!(updated_at: 1.minute.ago)
-        StuckCiBuildsWorker.new.perform
+        worker.perform
         is_expected.to eq(status)
       end
     end
@@ -36,9 +37,21 @@ describe StuckCiBuildsWorker do
 
       it "is still #{status}" do
         build.update!(updated_at: 2.days.ago)
-        StuckCiBuildsWorker.new.perform
+        worker.perform
         is_expected.to eq(status)
       end
     end
   end
+
+  context "for deleted project" do
+    before do
+      build.update!(status: :running, updated_at: 2.days.ago)
+      build.project.update(pending_delete: true)
+    end
+
+    it "does not drop build" do
+      expect_any_instance_of(Ci::Build).not_to receive(:drop)
+      worker.perform
+    end
+  end
 end
diff --git a/spec/workers/update_all_mirrors_worker_spec.rb b/spec/workers/update_all_mirrors_worker_spec.rb
index 88c11fe80629337c51baabf70a574f48ca1c97a1..870bec3e4ed8372857da6b61878ae3e38287b63e 100644
--- a/spec/workers/update_all_mirrors_worker_spec.rb
+++ b/spec/workers/update_all_mirrors_worker_spec.rb
@@ -1,6 +1,11 @@
 require 'rails_helper'
 
 describe UpdateAllMirrorsWorker do
+  before do
+    allow_any_instance_of(Gitlab::ExclusiveLease)
+      .to receive(:try_obtain).and_return(true)
+  end
+
   describe '#perform' do
     it 'fails stuck mirrors' do
       worker = described_class.new
@@ -11,12 +16,27 @@ describe UpdateAllMirrorsWorker do
     end
 
     it 'updates all mirrored Projects' do
+      worker = described_class.new
+
       create(:empty_project, :mirror)
       create(:empty_project)
 
-      expect_any_instance_of(Project).to receive(:update_mirror).once
+      expect(worker).to receive(:rand).with(30.minutes).and_return(10)
+      expect_any_instance_of(Project).to receive(:update_mirror).with(delay: 10).once
+
+      worker.perform
+    end
 
-      described_class.new.perform
+    it 'does not execute if cannot get the lease' do
+      allow_any_instance_of(Gitlab::ExclusiveLease)
+        .to receive(:try_obtain).and_return(false)
+
+      worker = described_class.new
+      create(:empty_project, :mirror)
+
+      expect(worker).not_to receive(:fail_stuck_mirrors!)
+
+      worker.perform
     end
   end
 
diff --git a/vendor/assets/javascripts/raphael.js b/vendor/assets/javascripts/raphael.js
new file mode 100644
index 0000000000000000000000000000000000000000..3f3f8a0b7f63af994df8a5e24afc521105296ba1
--- /dev/null
+++ b/vendor/assets/javascripts/raphael.js
@@ -0,0 +1,8239 @@
+// ┌────────────────────────────────────────────────────────────────────┐ \\
+// │ Raphaël 2.1.4 - JavaScript Vector Library                          │ \\
+// ├────────────────────────────────────────────────────────────────────┤ \\
+// │ Copyright © 2008-2012 Dmitry Baranovskiy (http://raphaeljs.com)    │ \\
+// │ Copyright © 2008-2012 Sencha Labs (http://sencha.com)              │ \\
+// ├────────────────────────────────────────────────────────────────────┤ \\
+// │ Licensed under the MIT (http://raphaeljs.com/license.html) license.│ \\
+// └────────────────────────────────────────────────────────────────────┘ \\
+// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
+// 
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// 
+// http://www.apache.org/licenses/LICENSE-2.0
+// 
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ┌────────────────────────────────────────────────────────────┐ \\
+// │ Eve 0.4.2 - JavaScript Events Library                      │ \\
+// ├────────────────────────────────────────────────────────────┤ \\
+// │ Author Dmitry Baranovskiy (http://dmitry.baranovskiy.com/) │ \\
+// └────────────────────────────────────────────────────────────┘ \\
+
+(function (glob) {
+    var version = "0.4.2",
+        has = "hasOwnProperty",
+        separator = /[\.\/]/,
+        wildcard = "*",
+        fun = function () {},
+        numsort = function (a, b) {
+            return a - b;
+        },
+        current_event,
+        stop,
+        events = {n: {}},
+    /*\
+     * eve
+     [ method ]
+
+     * Fires event with given `name`, given scope and other parameters.
+
+     > Arguments
+
+     - name (string) name of the *event*, dot (`.`) or slash (`/`) separated
+     - scope (object) context for the event handlers
+     - varargs (...) the rest of arguments will be sent to event handlers
+
+     = (object) array of returned values from the listeners
+    \*/
+        eve = function (name, scope) {
+			name = String(name);
+            var e = events,
+                oldstop = stop,
+                args = Array.prototype.slice.call(arguments, 2),
+                listeners = eve.listeners(name),
+                z = 0,
+                f = false,
+                l,
+                indexed = [],
+                queue = {},
+                out = [],
+                ce = current_event,
+                errors = [];
+            current_event = name;
+            stop = 0;
+            for (var i = 0, ii = listeners.length; i < ii; i++) if ("zIndex" in listeners[i]) {
+                indexed.push(listeners[i].zIndex);
+                if (listeners[i].zIndex < 0) {
+                    queue[listeners[i].zIndex] = listeners[i];
+                }
+            }
+            indexed.sort(numsort);
+            while (indexed[z] < 0) {
+                l = queue[indexed[z++]];
+                out.push(l.apply(scope, args));
+                if (stop) {
+                    stop = oldstop;
+                    return out;
+                }
+            }
+            for (i = 0; i < ii; i++) {
+                l = listeners[i];
+                if ("zIndex" in l) {
+                    if (l.zIndex == indexed[z]) {
+                        out.push(l.apply(scope, args));
+                        if (stop) {
+                            break;
+                        }
+                        do {
+                            z++;
+                            l = queue[indexed[z]];
+                            l && out.push(l.apply(scope, args));
+                            if (stop) {
+                                break;
+                            }
+                        } while (l)
+                    } else {
+                        queue[l.zIndex] = l;
+                    }
+                } else {
+                    out.push(l.apply(scope, args));
+                    if (stop) {
+                        break;
+                    }
+                }
+            }
+            stop = oldstop;
+            current_event = ce;
+            return out.length ? out : null;
+        };
+		// Undocumented. Debug only.
+		eve._events = events;
+    /*\
+     * eve.listeners
+     [ method ]
+
+     * Internal method which gives you array of all event handlers that will be triggered by the given `name`.
+
+     > Arguments
+
+     - name (string) name of the event, dot (`.`) or slash (`/`) separated
+
+     = (array) array of event handlers
+    \*/
+    eve.listeners = function (name) {
+        var names = name.split(separator),
+            e = events,
+            item,
+            items,
+            k,
+            i,
+            ii,
+            j,
+            jj,
+            nes,
+            es = [e],
+            out = [];
+        for (i = 0, ii = names.length; i < ii; i++) {
+            nes = [];
+            for (j = 0, jj = es.length; j < jj; j++) {
+                e = es[j].n;
+                items = [e[names[i]], e[wildcard]];
+                k = 2;
+                while (k--) {
+                    item = items[k];
+                    if (item) {
+                        nes.push(item);
+                        out = out.concat(item.f || []);
+                    }
+                }
+            }
+            es = nes;
+        }
+        return out;
+    };
+    
+    /*\
+     * eve.on
+     [ method ]
+     **
+     * Binds given event handler with a given name. You can use wildcards “`*`” for the names:
+     | eve.on("*.under.*", f);
+     | eve("mouse.under.floor"); // triggers f
+     * Use @eve to trigger the listener.
+     **
+     > Arguments
+     **
+     - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
+     - f (function) event handler function
+     **
+     = (function) returned function accepts a single numeric parameter that represents z-index of the handler. It is an optional feature and only used when you need to ensure that some subset of handlers will be invoked in a given order, despite of the order of assignment. 
+     > Example:
+     | eve.on("mouse", eatIt)(2);
+     | eve.on("mouse", scream);
+     | eve.on("mouse", catchIt)(1);
+     * This will ensure that `catchIt()` function will be called before `eatIt()`.
+	 *
+     * If you want to put your handler before non-indexed handlers, specify a negative value.
+     * Note: I assume most of the time you don’t need to worry about z-index, but it’s nice to have this feature “just in case”.
+    \*/
+    eve.on = function (name, f) {
+		name = String(name);
+		if (typeof f != "function") {
+			return function () {};
+		}
+        var names = name.split(separator),
+            e = events;
+        for (var i = 0, ii = names.length; i < ii; i++) {
+            e = e.n;
+            e = e.hasOwnProperty(names[i]) && e[names[i]] || (e[names[i]] = {n: {}});
+        }
+        e.f = e.f || [];
+        for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) {
+            return fun;
+        }
+        e.f.push(f);
+        return function (zIndex) {
+            if (+zIndex == +zIndex) {
+                f.zIndex = +zIndex;
+            }
+        };
+    };
+    /*\
+     * eve.f
+     [ method ]
+     **
+     * Returns function that will fire given event with optional arguments.
+	 * Arguments that will be passed to the result function will be also
+	 * concated to the list of final arguments.
+ 	 | el.onclick = eve.f("click", 1, 2);
+ 	 | eve.on("click", function (a, b, c) {
+ 	 |     console.log(a, b, c); // 1, 2, [event object]
+ 	 | });
+     > Arguments
+	 - event (string) event name
+	 - varargs (…) and any other arguments
+	 = (function) possible event handler function
+    \*/
+	eve.f = function (event) {
+		var attrs = [].slice.call(arguments, 1);
+		return function () {
+			eve.apply(null, [event, null].concat(attrs).concat([].slice.call(arguments, 0)));
+		};
+	};
+    /*\
+     * eve.stop
+     [ method ]
+     **
+     * Is used inside an event handler to stop the event, preventing any subsequent listeners from firing.
+    \*/
+    eve.stop = function () {
+        stop = 1;
+    };
+    /*\
+     * eve.nt
+     [ method ]
+     **
+     * Could be used inside event handler to figure out actual name of the event.
+     **
+     > Arguments
+     **
+     - subname (string) #optional subname of the event
+     **
+     = (string) name of the event, if `subname` is not specified
+     * or
+     = (boolean) `true`, if current event’s name contains `subname`
+    \*/
+    eve.nt = function (subname) {
+        if (subname) {
+            return new RegExp("(?:\\.|\\/|^)" + subname + "(?:\\.|\\/|$)").test(current_event);
+        }
+        return current_event;
+    };
+    /*\
+     * eve.nts
+     [ method ]
+     **
+     * Could be used inside event handler to figure out actual name of the event.
+     **
+     **
+     = (array) names of the event
+    \*/
+    eve.nts = function () {
+        return current_event.split(separator);
+    };
+    /*\
+     * eve.off
+     [ method ]
+     **
+     * Removes given function from the list of event listeners assigned to given name.
+	 * If no arguments specified all the events will be cleared.
+     **
+     > Arguments
+     **
+     - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
+     - f (function) event handler function
+    \*/
+    /*\
+     * eve.unbind
+     [ method ]
+     **
+     * See @eve.off
+    \*/
+    eve.off = eve.unbind = function (name, f) {
+		if (!name) {
+		    eve._events = events = {n: {}};
+			return;
+		}
+        var names = name.split(separator),
+            e,
+            key,
+            splice,
+            i, ii, j, jj,
+            cur = [events];
+        for (i = 0, ii = names.length; i < ii; i++) {
+            for (j = 0; j < cur.length; j += splice.length - 2) {
+                splice = [j, 1];
+                e = cur[j].n;
+                if (names[i] != wildcard) {
+                    if (e[names[i]]) {
+                        splice.push(e[names[i]]);
+                    }
+                } else {
+                    for (key in e) if (e[has](key)) {
+                        splice.push(e[key]);
+                    }
+                }
+                cur.splice.apply(cur, splice);
+            }
+        }
+        for (i = 0, ii = cur.length; i < ii; i++) {
+            e = cur[i];
+            while (e.n) {
+                if (f) {
+                    if (e.f) {
+                        for (j = 0, jj = e.f.length; j < jj; j++) if (e.f[j] == f) {
+                            e.f.splice(j, 1);
+                            break;
+                        }
+                        !e.f.length && delete e.f;
+                    }
+                    for (key in e.n) if (e.n[has](key) && e.n[key].f) {
+                        var funcs = e.n[key].f;
+                        for (j = 0, jj = funcs.length; j < jj; j++) if (funcs[j] == f) {
+                            funcs.splice(j, 1);
+                            break;
+                        }
+                        !funcs.length && delete e.n[key].f;
+                    }
+                } else {
+                    delete e.f;
+                    for (key in e.n) if (e.n[has](key) && e.n[key].f) {
+                        delete e.n[key].f;
+                    }
+                }
+                e = e.n;
+            }
+        }
+    };
+    /*\
+     * eve.once
+     [ method ]
+     **
+     * Binds given event handler with a given name to only run once then unbind itself.
+     | eve.once("login", f);
+     | eve("login"); // triggers f
+     | eve("login"); // no listeners
+     * Use @eve to trigger the listener.
+     **
+     > Arguments
+     **
+     - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
+     - f (function) event handler function
+     **
+     = (function) same return function as @eve.on
+    \*/
+    eve.once = function (name, f) {
+        var f2 = function () {
+            eve.unbind(name, f2);
+            return f.apply(this, arguments);
+        };
+        return eve.on(name, f2);
+    };
+    /*\
+     * eve.version
+     [ property (string) ]
+     **
+     * Current version of the library.
+    \*/
+    eve.version = version;
+    eve.toString = function () {
+        return "You are running Eve " + version;
+    };
+    (typeof module != "undefined" && module.exports) ? (module.exports = eve) : (typeof define != "undefined" ? (define("eve", [], function() { return eve; })) : (glob.eve = eve));
+})(window || this);
+// ┌─────────────────────────────────────────────────────────────────────┐ \\
+// │ "Raphaël 2.1.2" - JavaScript Vector Library                         │ \\
+// ├─────────────────────────────────────────────────────────────────────┤ \\
+// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com)   │ \\
+// │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com)             │ \\
+// │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
+// └─────────────────────────────────────────────────────────────────────┘ \\
+
+(function (glob, factory) {
+    // AMD support
+    if (typeof define === "function" && define.amd) {
+        // Define as an anonymous module
+        define(["eve"], function( eve ) {
+            return factory(glob, eve);
+        });
+    } else {
+        // Browser globals (glob is window)
+        // Raphael adds itself to window
+        factory(glob, glob.eve || (typeof require == "function" && require('eve')) );
+    }
+}(this, function (window, eve) {
+    /*\
+     * Raphael
+     [ method ]
+     **
+     * Creates a canvas object on which to draw.
+     * You must do this first, as all future calls to drawing methods
+     * from this instance will be bound to this canvas.
+     > Parameters
+     **
+     - container (HTMLElement|string) DOM element or its ID which is going to be a parent for drawing surface
+     - width (number)
+     - height (number)
+     - callback (function) #optional callback function which is going to be executed in the context of newly created paper
+     * or
+     - x (number)
+     - y (number)
+     - width (number)
+     - height (number)
+     - callback (function) #optional callback function which is going to be executed in the context of newly created paper
+     * or
+     - all (array) (first 3 or 4 elements in the array are equal to [containerID, width, height] or [x, y, width, height]. The rest are element descriptions in format {type: type, <attributes>}). See @Paper.add.
+     - callback (function) #optional callback function which is going to be executed in the context of newly created paper
+     * or
+     - onReadyCallback (function) function that is going to be called on DOM ready event. You can also subscribe to this event via Eve’s “DOMLoad” event. In this case method returns `undefined`.
+     = (object) @Paper
+     > Usage
+     | // Each of the following examples create a canvas
+     | // that is 320px wide by 200px high.
+     | // Canvas is created at the viewport’s 10,50 coordinate.
+     | var paper = Raphael(10, 50, 320, 200);
+     | // Canvas is created at the top left corner of the #notepad element
+     | // (or its top right corner in dir="rtl" elements)
+     | var paper = Raphael(document.getElementById("notepad"), 320, 200);
+     | // Same as above
+     | var paper = Raphael("notepad", 320, 200);
+     | // Image dump
+     | var set = Raphael(["notepad", 320, 200, {
+     |     type: "rect",
+     |     x: 10,
+     |     y: 10,
+     |     width: 25,
+     |     height: 25,
+     |     stroke: "#f00"
+     | }, {
+     |     type: "text",
+     |     x: 30,
+     |     y: 40,
+     |     text: "Dump"
+     | }]);
+    \*/
+    function R(first) {
+        if (R.is(first, "function")) {
+            return loaded ? first() : eve.on("raphael.DOMload", first);
+        } else if (R.is(first, array)) {
+            return R._engine.create[apply](R, first.splice(0, 3 + R.is(first[0], nu))).add(first);
+        } else {
+            var args = Array.prototype.slice.call(arguments, 0);
+            if (R.is(args[args.length - 1], "function")) {
+                var f = args.pop();
+                return loaded ? f.call(R._engine.create[apply](R, args)) : eve.on("raphael.DOMload", function () {
+                    f.call(R._engine.create[apply](R, args));
+                });
+            } else {
+                return R._engine.create[apply](R, arguments);
+            }
+        }
+    }
+    R.version = "2.1.2";
+    R.eve = eve;
+    var loaded,
+        separator = /[, ]+/,
+        elements = {circle: 1, rect: 1, path: 1, ellipse: 1, text: 1, image: 1},
+        formatrg = /\{(\d+)\}/g,
+        proto = "prototype",
+        has = "hasOwnProperty",
+        g = {
+            doc: document,
+            win: window
+        },
+        oldRaphael = {
+            was: Object.prototype[has].call(g.win, "Raphael"),
+            is: g.win.Raphael
+        },
+        Paper = function () {
+            /*\
+             * Paper.ca
+             [ property (object) ]
+             **
+             * Shortcut for @Paper.customAttributes
+            \*/
+            /*\
+             * Paper.customAttributes
+             [ property (object) ]
+             **
+             * If you have a set of attributes that you would like to represent
+             * as a function of some number you can do it easily with custom attributes:
+             > Usage
+             | paper.customAttributes.hue = function (num) {
+             |     num = num % 1;
+             |     return {fill: "hsb(" + num + ", 0.75, 1)"};
+             | };
+             | // Custom attribute “hue” will change fill
+             | // to be given hue with fixed saturation and brightness.
+             | // Now you can use it like this:
+             | var c = paper.circle(10, 10, 10).attr({hue: .45});
+             | // or even like this:
+             | c.animate({hue: 1}, 1e3);
+             |
+             | // You could also create custom attribute
+             | // with multiple parameters:
+             | paper.customAttributes.hsb = function (h, s, b) {
+             |     return {fill: "hsb(" + [h, s, b].join(",") + ")"};
+             | };
+             | c.attr({hsb: "0.5 .8 1"});
+             | c.animate({hsb: [1, 0, 0.5]}, 1e3);
+            \*/
+            this.ca = this.customAttributes = {};
+        },
+        paperproto,
+        appendChild = "appendChild",
+        apply = "apply",
+        concat = "concat",
+        supportsTouch = ('ontouchstart' in g.win) || g.win.DocumentTouch && g.doc instanceof DocumentTouch, //taken from Modernizr touch test
+        E = "",
+        S = " ",
+        Str = String,
+        split = "split",
+        events = "click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend touchcancel"[split](S),
+        touchMap = {
+            mousedown: "touchstart",
+            mousemove: "touchmove",
+            mouseup: "touchend"
+        },
+        lowerCase = Str.prototype.toLowerCase,
+        math = Math,
+        mmax = math.max,
+        mmin = math.min,
+        abs = math.abs,
+        pow = math.pow,
+        PI = math.PI,
+        nu = "number",
+        string = "string",
+        array = "array",
+        toString = "toString",
+        fillString = "fill",
+        objectToString = Object.prototype.toString,
+        paper = {},
+        push = "push",
+        ISURL = R._ISURL = /^url\(['"]?(.+?)['"]?\)$/i,
+        colourRegExp = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i,
+        isnan = {"NaN": 1, "Infinity": 1, "-Infinity": 1},
+        bezierrg = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,
+        round = math.round,
+        setAttribute = "setAttribute",
+        toFloat = parseFloat,
+        toInt = parseInt,
+        upperCase = Str.prototype.toUpperCase,
+        availableAttrs = R._availableAttrs = {
+            "arrow-end": "none",
+            "arrow-start": "none",
+            blur: 0,
+            "clip-rect": "0 0 1e9 1e9",
+            cursor: "default",
+            cx: 0,
+            cy: 0,
+            fill: "#fff",
+            "fill-opacity": 1,
+            font: '10px "Arial"',
+            "font-family": '"Arial"',
+            "font-size": "10",
+            "font-style": "normal",
+            "font-weight": 400,
+            gradient: 0,
+            height: 0,
+            href: "http://raphaeljs.com/",
+            "letter-spacing": 0,
+            opacity: 1,
+            path: "M0,0",
+            r: 0,
+            rx: 0,
+            ry: 0,
+            src: "",
+            stroke: "#000",
+            "stroke-dasharray": "",
+            "stroke-linecap": "butt",
+            "stroke-linejoin": "butt",
+            "stroke-miterlimit": 0,
+            "stroke-opacity": 1,
+            "stroke-width": 1,
+            target: "_blank",
+            "text-anchor": "middle",
+            title: "Raphael",
+            transform: "",
+            width: 0,
+            x: 0,
+            y: 0
+        },
+        availableAnimAttrs = R._availableAnimAttrs = {
+            blur: nu,
+            "clip-rect": "csv",
+            cx: nu,
+            cy: nu,
+            fill: "colour",
+            "fill-opacity": nu,
+            "font-size": nu,
+            height: nu,
+            opacity: nu,
+            path: "path",
+            r: nu,
+            rx: nu,
+            ry: nu,
+            stroke: "colour",
+            "stroke-opacity": nu,
+            "stroke-width": nu,
+            transform: "transform",
+            width: nu,
+            x: nu,
+            y: nu
+        },
+        whitespace = /[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]/g,
+        commaSpaces = /[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/,
+        hsrg = {hs: 1, rg: 1},
+        p2s = /,?([achlmqrstvxz]),?/gi,
+        pathCommand = /([achlmrqstvz])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/ig,
+        tCommand = /([rstm])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/ig,
+        pathValues = /(-?\d*\.?\d*(?:e[\-+]?\d+)?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/ig,
+        radial_gradient = R._radial_gradient = /^r(?:\(([^,]+?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*([^\)]+?)\))?/,
+        eldata = {},
+        sortByKey = function (a, b) {
+            return a.key - b.key;
+        },
+        sortByNumber = function (a, b) {
+            return toFloat(a) - toFloat(b);
+        },
+        fun = function () {},
+        pipe = function (x) {
+            return x;
+        },
+        rectPath = R._rectPath = function (x, y, w, h, r) {
+            if (r) {
+                return [["M", x + r, y], ["l", w - r * 2, 0], ["a", r, r, 0, 0, 1, r, r], ["l", 0, h - r * 2], ["a", r, r, 0, 0, 1, -r, r], ["l", r * 2 - w, 0], ["a", r, r, 0, 0, 1, -r, -r], ["l", 0, r * 2 - h], ["a", r, r, 0, 0, 1, r, -r], ["z"]];
+            }
+            return [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]];
+        },
+        ellipsePath = function (x, y, rx, ry) {
+            if (ry == null) {
+                ry = rx;
+            }
+            return [["M", x, y], ["m", 0, -ry], ["a", rx, ry, 0, 1, 1, 0, 2 * ry], ["a", rx, ry, 0, 1, 1, 0, -2 * ry], ["z"]];
+        },
+        getPath = R._getPath = {
+            path: function (el) {
+                return el.attr("path");
+            },
+            circle: function (el) {
+                var a = el.attrs;
+                return ellipsePath(a.cx, a.cy, a.r);
+            },
+            ellipse: function (el) {
+                var a = el.attrs;
+                return ellipsePath(a.cx, a.cy, a.rx, a.ry);
+            },
+            rect: function (el) {
+                var a = el.attrs;
+                return rectPath(a.x, a.y, a.width, a.height, a.r);
+            },
+            image: function (el) {
+                var a = el.attrs;
+                return rectPath(a.x, a.y, a.width, a.height);
+            },
+            text: function (el) {
+                var bbox = el._getBBox();
+                return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
+            },
+            set : function(el) {
+                var bbox = el._getBBox();
+                return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
+            }
+        },
+        /*\
+         * Raphael.mapPath
+         [ method ]
+         **
+         * Transform the path string with given matrix.
+         > Parameters
+         - path (string) path string
+         - matrix (object) see @Matrix
+         = (string) transformed path string
+        \*/
+        mapPath = R.mapPath = function (path, matrix) {
+            if (!matrix) {
+                return path;
+            }
+            var x, y, i, j, ii, jj, pathi;
+            path = path2curve(path);
+            for (i = 0, ii = path.length; i < ii; i++) {
+                pathi = path[i];
+                for (j = 1, jj = pathi.length; j < jj; j += 2) {
+                    x = matrix.x(pathi[j], pathi[j + 1]);
+                    y = matrix.y(pathi[j], pathi[j + 1]);
+                    pathi[j] = x;
+                    pathi[j + 1] = y;
+                }
+            }
+            return path;
+        };
+
+    R._g = g;
+    /*\
+     * Raphael.type
+     [ property (string) ]
+     **
+     * Can be “SVG”, “VML” or empty, depending on browser support.
+    \*/
+    R.type = (g.win.SVGAngle || g.doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML");
+    if (R.type == "VML") {
+        var d = g.doc.createElement("div"),
+            b;
+        d.innerHTML = '<v:shape adj="1"/>';
+        b = d.firstChild;
+        b.style.behavior = "url(#default#VML)";
+        if (!(b && typeof b.adj == "object")) {
+            return (R.type = E);
+        }
+        d = null;
+    }
+    /*\
+     * Raphael.svg
+     [ property (boolean) ]
+     **
+     * `true` if browser supports SVG.
+    \*/
+    /*\
+     * Raphael.vml
+     [ property (boolean) ]
+     **
+     * `true` if browser supports VML.
+    \*/
+    R.svg = !(R.vml = R.type == "VML");
+    R._Paper = Paper;
+    /*\
+     * Raphael.fn
+     [ property (object) ]
+     **
+     * You can add your own method to the canvas. For example if you want to draw a pie chart,
+     * you can create your own pie chart function and ship it as a Raphaël plugin. To do this
+     * you need to extend the `Raphael.fn` object. You should modify the `fn` object before a
+     * Raphaël instance is created, otherwise it will take no effect. Please note that the
+     * ability for namespaced plugins was removed in Raphael 2.0. It is up to the plugin to
+     * ensure any namespacing ensures proper context.
+     > Usage
+     | Raphael.fn.arrow = function (x1, y1, x2, y2, size) {
+     |     return this.path( ... );
+     | };
+     | // or create namespace
+     | Raphael.fn.mystuff = {
+     |     arrow: function () {…},
+     |     star: function () {…},
+     |     // etc…
+     | };
+     | var paper = Raphael(10, 10, 630, 480);
+     | // then use it
+     | paper.arrow(10, 10, 30, 30, 5).attr({fill: "#f00"});
+     | paper.mystuff.arrow();
+     | paper.mystuff.star();
+    \*/
+    R.fn = paperproto = Paper.prototype = R.prototype;
+    R._id = 0;
+    R._oid = 0;
+    /*\
+     * Raphael.is
+     [ method ]
+     **
+     * Handful of replacements for `typeof` operator.
+     > Parameters
+     - o (…) any object or primitive
+     - type (string) name of the type, i.e. “string”, “function”, “number”, etc.
+     = (boolean) is given value is of given type
+    \*/
+    R.is = function (o, type) {
+        type = lowerCase.call(type);
+        if (type == "finite") {
+            return !isnan[has](+o);
+        }
+        if (type == "array") {
+            return o instanceof Array;
+        }
+        return  (type == "null" && o === null) ||
+                (type == typeof o && o !== null) ||
+                (type == "object" && o === Object(o)) ||
+                (type == "array" && Array.isArray && Array.isArray(o)) ||
+                objectToString.call(o).slice(8, -1).toLowerCase() == type;
+    };
+
+    function clone(obj) {
+        if (typeof obj == "function" || Object(obj) !== obj) {
+            return obj;
+        }
+        var res = new obj.constructor;
+        for (var key in obj) if (obj[has](key)) {
+            res[key] = clone(obj[key]);
+        }
+        return res;
+    }
+
+    /*\
+     * Raphael.angle
+     [ method ]
+     **
+     * Returns angle between two or three points
+     > Parameters
+     - x1 (number) x coord of first point
+     - y1 (number) y coord of first point
+     - x2 (number) x coord of second point
+     - y2 (number) y coord of second point
+     - x3 (number) #optional x coord of third point
+     - y3 (number) #optional y coord of third point
+     = (number) angle in degrees.
+    \*/
+    R.angle = function (x1, y1, x2, y2, x3, y3) {
+        if (x3 == null) {
+            var x = x1 - x2,
+                y = y1 - y2;
+            if (!x && !y) {
+                return 0;
+            }
+            return (180 + math.atan2(-y, -x) * 180 / PI + 360) % 360;
+        } else {
+            return R.angle(x1, y1, x3, y3) - R.angle(x2, y2, x3, y3);
+        }
+    };
+    /*\
+     * Raphael.rad
+     [ method ]
+     **
+     * Transform angle to radians
+     > Parameters
+     - deg (number) angle in degrees
+     = (number) angle in radians.
+    \*/
+    R.rad = function (deg) {
+        return deg % 360 * PI / 180;
+    };
+    /*\
+     * Raphael.deg
+     [ method ]
+     **
+     * Transform angle to degrees
+     > Parameters
+     - rad (number) angle in radians
+     = (number) angle in degrees.
+    \*/
+    R.deg = function (rad) {
+        return Math.round ((rad * 180 / PI% 360)* 1000) / 1000;
+    };
+    /*\
+     * Raphael.snapTo
+     [ method ]
+     **
+     * Snaps given value to given grid.
+     > Parameters
+     - values (array|number) given array of values or step of the grid
+     - value (number) value to adjust
+     - tolerance (number) #optional tolerance for snapping. Default is `10`.
+     = (number) adjusted value.
+    \*/
+    R.snapTo = function (values, value, tolerance) {
+        tolerance = R.is(tolerance, "finite") ? tolerance : 10;
+        if (R.is(values, array)) {
+            var i = values.length;
+            while (i--) if (abs(values[i] - value) <= tolerance) {
+                return values[i];
+            }
+        } else {
+            values = +values;
+            var rem = value % values;
+            if (rem < tolerance) {
+                return value - rem;
+            }
+            if (rem > values - tolerance) {
+                return value - rem + values;
+            }
+        }
+        return value;
+    };
+
+    /*\
+     * Raphael.createUUID
+     [ method ]
+     **
+     * Returns RFC4122, version 4 ID
+    \*/
+    var createUUID = R.createUUID = (function (uuidRegEx, uuidReplacer) {
+        return function () {
+            return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(uuidRegEx, uuidReplacer).toUpperCase();
+        };
+    })(/[xy]/g, function (c) {
+        var r = math.random() * 16 | 0,
+            v = c == "x" ? r : (r & 3 | 8);
+        return v.toString(16);
+    });
+
+    /*\
+     * Raphael.setWindow
+     [ method ]
+     **
+     * Used when you need to draw in `&lt;iframe>`. Switched window to the iframe one.
+     > Parameters
+     - newwin (window) new window object
+    \*/
+    R.setWindow = function (newwin) {
+        eve("raphael.setWindow", R, g.win, newwin);
+        g.win = newwin;
+        g.doc = g.win.document;
+        if (R._engine.initWin) {
+            R._engine.initWin(g.win);
+        }
+    };
+    var toHex = function (color) {
+        if (R.vml) {
+            // http://dean.edwards.name/weblog/2009/10/convert-any-colour-value-to-hex-in-msie/
+            var trim = /^\s+|\s+$/g;
+            var bod;
+            try {
+                var docum = new ActiveXObject("htmlfile");
+                docum.write("<body>");
+                docum.close();
+                bod = docum.body;
+            } catch(e) {
+                bod = createPopup().document.body;
+            }
+            var range = bod.createTextRange();
+            toHex = cacher(function (color) {
+                try {
+                    bod.style.color = Str(color).replace(trim, E);
+                    var value = range.queryCommandValue("ForeColor");
+                    value = ((value & 255) << 16) | (value & 65280) | ((value & 16711680) >>> 16);
+                    return "#" + ("000000" + value.toString(16)).slice(-6);
+                } catch(e) {
+                    return "none";
+                }
+            });
+        } else {
+            var i = g.doc.createElement("i");
+            i.title = "Rapha\xebl Colour Picker";
+            i.style.display = "none";
+            g.doc.body.appendChild(i);
+            toHex = cacher(function (color) {
+                i.style.color = color;
+                return g.doc.defaultView.getComputedStyle(i, E).getPropertyValue("color");
+            });
+        }
+        return toHex(color);
+    },
+    hsbtoString = function () {
+        return "hsb(" + [this.h, this.s, this.b] + ")";
+    },
+    hsltoString = function () {
+        return "hsl(" + [this.h, this.s, this.l] + ")";
+    },
+    rgbtoString = function () {
+        return this.hex;
+    },
+    prepareRGB = function (r, g, b) {
+        if (g == null && R.is(r, "object") && "r" in r && "g" in r && "b" in r) {
+            b = r.b;
+            g = r.g;
+            r = r.r;
+        }
+        if (g == null && R.is(r, string)) {
+            var clr = R.getRGB(r);
+            r = clr.r;
+            g = clr.g;
+            b = clr.b;
+        }
+        if (r > 1 || g > 1 || b > 1) {
+            r /= 255;
+            g /= 255;
+            b /= 255;
+        }
+
+        return [r, g, b];
+    },
+    packageRGB = function (r, g, b, o) {
+        r *= 255;
+        g *= 255;
+        b *= 255;
+        var rgb = {
+            r: r,
+            g: g,
+            b: b,
+            hex: R.rgb(r, g, b),
+            toString: rgbtoString
+        };
+        R.is(o, "finite") && (rgb.opacity = o);
+        return rgb;
+    };
+
+    /*\
+     * Raphael.color
+     [ method ]
+     **
+     * Parses the color string and returns object with all values for the given color.
+     > Parameters
+     - clr (string) color string in one of the supported formats (see @Raphael.getRGB)
+     = (object) Combined RGB & HSB object in format:
+     o {
+     o     r (number) red,
+     o     g (number) green,
+     o     b (number) blue,
+     o     hex (string) color in HTML/CSS format: #••••••,
+     o     error (boolean) `true` if string can’t be parsed,
+     o     h (number) hue,
+     o     s (number) saturation,
+     o     v (number) value (brightness),
+     o     l (number) lightness
+     o }
+    \*/
+    R.color = function (clr) {
+        var rgb;
+        if (R.is(clr, "object") && "h" in clr && "s" in clr && "b" in clr) {
+            rgb = R.hsb2rgb(clr);
+            clr.r = rgb.r;
+            clr.g = rgb.g;
+            clr.b = rgb.b;
+            clr.hex = rgb.hex;
+        } else if (R.is(clr, "object") && "h" in clr && "s" in clr && "l" in clr) {
+            rgb = R.hsl2rgb(clr);
+            clr.r = rgb.r;
+            clr.g = rgb.g;
+            clr.b = rgb.b;
+            clr.hex = rgb.hex;
+        } else {
+            if (R.is(clr, "string")) {
+                clr = R.getRGB(clr);
+            }
+            if (R.is(clr, "object") && "r" in clr && "g" in clr && "b" in clr) {
+                rgb = R.rgb2hsl(clr);
+                clr.h = rgb.h;
+                clr.s = rgb.s;
+                clr.l = rgb.l;
+                rgb = R.rgb2hsb(clr);
+                clr.v = rgb.b;
+            } else {
+                clr = {hex: "none"};
+                clr.r = clr.g = clr.b = clr.h = clr.s = clr.v = clr.l = -1;
+            }
+        }
+        clr.toString = rgbtoString;
+        return clr;
+    };
+    /*\
+     * Raphael.hsb2rgb
+     [ method ]
+     **
+     * Converts HSB values to RGB object.
+     > Parameters
+     - h (number) hue
+     - s (number) saturation
+     - v (number) value or brightness
+     = (object) RGB object in format:
+     o {
+     o     r (number) red,
+     o     g (number) green,
+     o     b (number) blue,
+     o     hex (string) color in HTML/CSS format: #••••••
+     o }
+    \*/
+    R.hsb2rgb = function (h, s, v, o) {
+        if (this.is(h, "object") && "h" in h && "s" in h && "b" in h) {
+            v = h.b;
+            s = h.s;
+            o = h.o;
+            h = h.h;
+        }
+        h *= 360;
+        var R, G, B, X, C;
+        h = (h % 360) / 60;
+        C = v * s;
+        X = C * (1 - abs(h % 2 - 1));
+        R = G = B = v - C;
+
+        h = ~~h;
+        R += [C, X, 0, 0, X, C][h];
+        G += [X, C, C, X, 0, 0][h];
+        B += [0, 0, X, C, C, X][h];
+        return packageRGB(R, G, B, o);
+    };
+    /*\
+     * Raphael.hsl2rgb
+     [ method ]
+     **
+     * Converts HSL values to RGB object.
+     > Parameters
+     - h (number) hue
+     - s (number) saturation
+     - l (number) luminosity
+     = (object) RGB object in format:
+     o {
+     o     r (number) red,
+     o     g (number) green,
+     o     b (number) blue,
+     o     hex (string) color in HTML/CSS format: #••••••
+     o }
+    \*/
+    R.hsl2rgb = function (h, s, l, o) {
+        if (this.is(h, "object") && "h" in h && "s" in h && "l" in h) {
+            l = h.l;
+            s = h.s;
+            h = h.h;
+        }
+        if (h > 1 || s > 1 || l > 1) {
+            h /= 360;
+            s /= 100;
+            l /= 100;
+        }
+        h *= 360;
+        var R, G, B, X, C;
+        h = (h % 360) / 60;
+        C = 2 * s * (l < .5 ? l : 1 - l);
+        X = C * (1 - abs(h % 2 - 1));
+        R = G = B = l - C / 2;
+
+        h = ~~h;
+        R += [C, X, 0, 0, X, C][h];
+        G += [X, C, C, X, 0, 0][h];
+        B += [0, 0, X, C, C, X][h];
+        return packageRGB(R, G, B, o);
+    };
+    /*\
+     * Raphael.rgb2hsb
+     [ method ]
+     **
+     * Converts RGB values to HSB object.
+     > Parameters
+     - r (number) red
+     - g (number) green
+     - b (number) blue
+     = (object) HSB object in format:
+     o {
+     o     h (number) hue
+     o     s (number) saturation
+     o     b (number) brightness
+     o }
+    \*/
+    R.rgb2hsb = function (r, g, b) {
+        b = prepareRGB(r, g, b);
+        r = b[0];
+        g = b[1];
+        b = b[2];
+
+        var H, S, V, C;
+        V = mmax(r, g, b);
+        C = V - mmin(r, g, b);
+        H = (C == 0 ? null :
+             V == r ? (g - b) / C :
+             V == g ? (b - r) / C + 2 :
+                      (r - g) / C + 4
+            );
+        H = ((H + 360) % 6) * 60 / 360;
+        S = C == 0 ? 0 : C / V;
+        return {h: H, s: S, b: V, toString: hsbtoString};
+    };
+    /*\
+     * Raphael.rgb2hsl
+     [ method ]
+     **
+     * Converts RGB values to HSL object.
+     > Parameters
+     - r (number) red
+     - g (number) green
+     - b (number) blue
+     = (object) HSL object in format:
+     o {
+     o     h (number) hue
+     o     s (number) saturation
+     o     l (number) luminosity
+     o }
+    \*/
+    R.rgb2hsl = function (r, g, b) {
+        b = prepareRGB(r, g, b);
+        r = b[0];
+        g = b[1];
+        b = b[2];
+
+        var H, S, L, M, m, C;
+        M = mmax(r, g, b);
+        m = mmin(r, g, b);
+        C = M - m;
+        H = (C == 0 ? null :
+             M == r ? (g - b) / C :
+             M == g ? (b - r) / C + 2 :
+                      (r - g) / C + 4);
+        H = ((H + 360) % 6) * 60 / 360;
+        L = (M + m) / 2;
+        S = (C == 0 ? 0 :
+             L < .5 ? C / (2 * L) :
+                      C / (2 - 2 * L));
+        return {h: H, s: S, l: L, toString: hsltoString};
+    };
+    R._path2string = function () {
+        return this.join(",").replace(p2s, "$1");
+    };
+    function repush(array, item) {
+        for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) {
+            return array.push(array.splice(i, 1)[0]);
+        }
+    }
+    function cacher(f, scope, postprocessor) {
+        function newf() {
+            var arg = Array.prototype.slice.call(arguments, 0),
+                args = arg.join("\u2400"),
+                cache = newf.cache = newf.cache || {},
+                count = newf.count = newf.count || [];
+            if (cache[has](args)) {
+                repush(count, args);
+                return postprocessor ? postprocessor(cache[args]) : cache[args];
+            }
+            count.length >= 1e3 && delete cache[count.shift()];
+            count.push(args);
+            cache[args] = f[apply](scope, arg);
+            return postprocessor ? postprocessor(cache[args]) : cache[args];
+        }
+        return newf;
+    }
+
+    var preload = R._preload = function (src, f) {
+        var img = g.doc.createElement("img");
+        img.style.cssText = "position:absolute;left:-9999em;top:-9999em";
+        img.onload = function () {
+            f.call(this);
+            this.onload = null;
+            g.doc.body.removeChild(this);
+        };
+        img.onerror = function () {
+            g.doc.body.removeChild(this);
+        };
+        g.doc.body.appendChild(img);
+        img.src = src;
+    };
+
+    function clrToString() {
+        return this.hex;
+    }
+
+    /*\
+     * Raphael.getRGB
+     [ method ]
+     **
+     * Parses colour string as RGB object
+     > Parameters
+     - colour (string) colour string in one of formats:
+     # <ul>
+     #     <li>Colour name (“<code>red</code>”, “<code>green</code>”, “<code>cornflowerblue</code>”, etc)</li>
+     #     <li>#••• — shortened HTML colour: (“<code>#000</code>”, “<code>#fc0</code>”, etc)</li>
+     #     <li>#•••••• — full length HTML colour: (“<code>#000000</code>”, “<code>#bd2300</code>”)</li>
+     #     <li>rgb(•••, •••, •••) — red, green and blue channels’ values: (“<code>rgb(200,&nbsp;100,&nbsp;0)</code>”)</li>
+     #     <li>rgb(•••%, •••%, •••%) — same as above, but in %: (“<code>rgb(100%,&nbsp;175%,&nbsp;0%)</code>”)</li>
+     #     <li>hsb(•••, •••, •••) — hue, saturation and brightness values: (“<code>hsb(0.5,&nbsp;0.25,&nbsp;1)</code>”)</li>
+     #     <li>hsb(•••%, •••%, •••%) — same as above, but in %</li>
+     #     <li>hsl(•••, •••, •••) — same as hsb</li>
+     #     <li>hsl(•••%, •••%, •••%) — same as hsb</li>
+     # </ul>
+     = (object) RGB object in format:
+     o {
+     o     r (number) red,
+     o     g (number) green,
+     o     b (number) blue
+     o     hex (string) color in HTML/CSS format: #••••••,
+     o     error (boolean) true if string can’t be parsed
+     o }
+    \*/
+    R.getRGB = cacher(function (colour) {
+        if (!colour || !!((colour = Str(colour)).indexOf("-") + 1)) {
+            return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: clrToString};
+        }
+        if (colour == "none") {
+            return {r: -1, g: -1, b: -1, hex: "none", toString: clrToString};
+        }
+        !(hsrg[has](colour.toLowerCase().substring(0, 2)) || colour.charAt() == "#") && (colour = toHex(colour));
+        var res,
+            red,
+            green,
+            blue,
+            opacity,
+            t,
+            values,
+            rgb = colour.match(colourRegExp);
+        if (rgb) {
+            if (rgb[2]) {
+                blue = toInt(rgb[2].substring(5), 16);
+                green = toInt(rgb[2].substring(3, 5), 16);
+                red = toInt(rgb[2].substring(1, 3), 16);
+            }
+            if (rgb[3]) {
+                blue = toInt((t = rgb[3].charAt(3)) + t, 16);
+                green = toInt((t = rgb[3].charAt(2)) + t, 16);
+                red = toInt((t = rgb[3].charAt(1)) + t, 16);
+            }
+            if (rgb[4]) {
+                values = rgb[4][split](commaSpaces);
+                red = toFloat(values[0]);
+                values[0].slice(-1) == "%" && (red *= 2.55);
+                green = toFloat(values[1]);
+                values[1].slice(-1) == "%" && (green *= 2.55);
+                blue = toFloat(values[2]);
+                values[2].slice(-1) == "%" && (blue *= 2.55);
+                rgb[1].toLowerCase().slice(0, 4) == "rgba" && (opacity = toFloat(values[3]));
+                values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
+            }
+            if (rgb[5]) {
+                values = rgb[5][split](commaSpaces);
+                red = toFloat(values[0]);
+                values[0].slice(-1) == "%" && (red *= 2.55);
+                green = toFloat(values[1]);
+                values[1].slice(-1) == "%" && (green *= 2.55);
+                blue = toFloat(values[2]);
+                values[2].slice(-1) == "%" && (blue *= 2.55);
+                (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
+                rgb[1].toLowerCase().slice(0, 4) == "hsba" && (opacity = toFloat(values[3]));
+                values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
+                return R.hsb2rgb(red, green, blue, opacity);
+            }
+            if (rgb[6]) {
+                values = rgb[6][split](commaSpaces);
+                red = toFloat(values[0]);
+                values[0].slice(-1) == "%" && (red *= 2.55);
+                green = toFloat(values[1]);
+                values[1].slice(-1) == "%" && (green *= 2.55);
+                blue = toFloat(values[2]);
+                values[2].slice(-1) == "%" && (blue *= 2.55);
+                (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
+                rgb[1].toLowerCase().slice(0, 4) == "hsla" && (opacity = toFloat(values[3]));
+                values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
+                return R.hsl2rgb(red, green, blue, opacity);
+            }
+            rgb = {r: red, g: green, b: blue, toString: clrToString};
+            rgb.hex = "#" + (16777216 | blue | (green << 8) | (red << 16)).toString(16).slice(1);
+            R.is(opacity, "finite") && (rgb.opacity = opacity);
+            return rgb;
+        }
+        return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: clrToString};
+    }, R);
+    /*\
+     * Raphael.hsb
+     [ method ]
+     **
+     * Converts HSB values to hex representation of the colour.
+     > Parameters
+     - h (number) hue
+     - s (number) saturation
+     - b (number) value or brightness
+     = (string) hex representation of the colour.
+    \*/
+    R.hsb = cacher(function (h, s, b) {
+        return R.hsb2rgb(h, s, b).hex;
+    });
+    /*\
+     * Raphael.hsl
+     [ method ]
+     **
+     * Converts HSL values to hex representation of the colour.
+     > Parameters
+     - h (number) hue
+     - s (number) saturation
+     - l (number) luminosity
+     = (string) hex representation of the colour.
+    \*/
+    R.hsl = cacher(function (h, s, l) {
+        return R.hsl2rgb(h, s, l).hex;
+    });
+    /*\
+     * Raphael.rgb
+     [ method ]
+     **
+     * Converts RGB values to hex representation of the colour.
+     > Parameters
+     - r (number) red
+     - g (number) green
+     - b (number) blue
+     = (string) hex representation of the colour.
+    \*/
+    R.rgb = cacher(function (r, g, b) {
+        return "#" + (16777216 | b | (g << 8) | (r << 16)).toString(16).slice(1);
+    });
+    /*\
+     * Raphael.getColor
+     [ method ]
+     **
+     * On each call returns next colour in the spectrum. To reset it back to red call @Raphael.getColor.reset
+     > Parameters
+     - value (number) #optional brightness, default is `0.75`
+     = (string) hex representation of the colour.
+    \*/
+    R.getColor = function (value) {
+        var start = this.getColor.start = this.getColor.start || {h: 0, s: 1, b: value || .75},
+            rgb = this.hsb2rgb(start.h, start.s, start.b);
+        start.h += .075;
+        if (start.h > 1) {
+            start.h = 0;
+            start.s -= .2;
+            start.s <= 0 && (this.getColor.start = {h: 0, s: 1, b: start.b});
+        }
+        return rgb.hex;
+    };
+    /*\
+     * Raphael.getColor.reset
+     [ method ]
+     **
+     * Resets spectrum position for @Raphael.getColor back to red.
+    \*/
+    R.getColor.reset = function () {
+        delete this.start;
+    };
+
+    // http://schepers.cc/getting-to-the-point
+    function catmullRom2bezier(crp, z) {
+        var d = [];
+        for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) {
+            var p = [
+                        {x: +crp[i - 2], y: +crp[i - 1]},
+                        {x: +crp[i],     y: +crp[i + 1]},
+                        {x: +crp[i + 2], y: +crp[i + 3]},
+                        {x: +crp[i + 4], y: +crp[i + 5]}
+                    ];
+            if (z) {
+                if (!i) {
+                    p[0] = {x: +crp[iLen - 2], y: +crp[iLen - 1]};
+                } else if (iLen - 4 == i) {
+                    p[3] = {x: +crp[0], y: +crp[1]};
+                } else if (iLen - 2 == i) {
+                    p[2] = {x: +crp[0], y: +crp[1]};
+                    p[3] = {x: +crp[2], y: +crp[3]};
+                }
+            } else {
+                if (iLen - 4 == i) {
+                    p[3] = p[2];
+                } else if (!i) {
+                    p[0] = {x: +crp[i], y: +crp[i + 1]};
+                }
+            }
+            d.push(["C",
+                  (-p[0].x + 6 * p[1].x + p[2].x) / 6,
+                  (-p[0].y + 6 * p[1].y + p[2].y) / 6,
+                  (p[1].x + 6 * p[2].x - p[3].x) / 6,
+                  (p[1].y + 6*p[2].y - p[3].y) / 6,
+                  p[2].x,
+                  p[2].y
+            ]);
+        }
+
+        return d;
+    }
+    /*\
+     * Raphael.parsePathString
+     [ method ]
+     **
+     * Utility method
+     **
+     * Parses given path string into an array of arrays of path segments.
+     > Parameters
+     - pathString (string|array) path string or array of segments (in the last case it will be returned straight away)
+     = (array) array of segments.
+    \*/
+    R.parsePathString = function (pathString) {
+        if (!pathString) {
+            return null;
+        }
+        var pth = paths(pathString);
+        if (pth.arr) {
+            return pathClone(pth.arr);
+        }
+
+        var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0},
+            data = [];
+        if (R.is(pathString, array) && R.is(pathString[0], array)) { // rough assumption
+            data = pathClone(pathString);
+        }
+        if (!data.length) {
+            Str(pathString).replace(pathCommand, function (a, b, c) {
+                var params = [],
+                    name = b.toLowerCase();
+                c.replace(pathValues, function (a, b) {
+                    b && params.push(+b);
+                });
+                if (name == "m" && params.length > 2) {
+                    data.push([b][concat](params.splice(0, 2)));
+                    name = "l";
+                    b = b == "m" ? "l" : "L";
+                }
+                if (name == "r") {
+                    data.push([b][concat](params));
+                } else while (params.length >= paramCounts[name]) {
+                    data.push([b][concat](params.splice(0, paramCounts[name])));
+                    if (!paramCounts[name]) {
+                        break;
+                    }
+                }
+            });
+        }
+        data.toString = R._path2string;
+        pth.arr = pathClone(data);
+        return data;
+    };
+    /*\
+     * Raphael.parseTransformString
+     [ method ]
+     **
+     * Utility method
+     **
+     * Parses given path string into an array of transformations.
+     > Parameters
+     - TString (string|array) transform string or array of transformations (in the last case it will be returned straight away)
+     = (array) array of transformations.
+    \*/
+    R.parseTransformString = cacher(function (TString) {
+        if (!TString) {
+            return null;
+        }
+        var paramCounts = {r: 3, s: 4, t: 2, m: 6},
+            data = [];
+        if (R.is(TString, array) && R.is(TString[0], array)) { // rough assumption
+            data = pathClone(TString);
+        }
+        if (!data.length) {
+            Str(TString).replace(tCommand, function (a, b, c) {
+                var params = [],
+                    name = lowerCase.call(b);
+                c.replace(pathValues, function (a, b) {
+                    b && params.push(+b);
+                });
+                data.push([b][concat](params));
+            });
+        }
+        data.toString = R._path2string;
+        return data;
+    });
+    // PATHS
+    var paths = function (ps) {
+        var p = paths.ps = paths.ps || {};
+        if (p[ps]) {
+            p[ps].sleep = 100;
+        } else {
+            p[ps] = {
+                sleep: 100
+            };
+        }
+        setTimeout(function () {
+            for (var key in p) if (p[has](key) && key != ps) {
+                p[key].sleep--;
+                !p[key].sleep && delete p[key];
+            }
+        });
+        return p[ps];
+    };
+    /*\
+     * Raphael.findDotsAtSegment
+     [ method ]
+     **
+     * Utility method
+     **
+     * Find dot coordinates on the given cubic bezier curve at the given t.
+     > Parameters
+     - p1x (number) x of the first point of the curve
+     - p1y (number) y of the first point of the curve
+     - c1x (number) x of the first anchor of the curve
+     - c1y (number) y of the first anchor of the curve
+     - c2x (number) x of the second anchor of the curve
+     - c2y (number) y of the second anchor of the curve
+     - p2x (number) x of the second point of the curve
+     - p2y (number) y of the second point of the curve
+     - t (number) position on the curve (0..1)
+     = (object) point information in format:
+     o {
+     o     x: (number) x coordinate of the point
+     o     y: (number) y coordinate of the point
+     o     m: {
+     o         x: (number) x coordinate of the left anchor
+     o         y: (number) y coordinate of the left anchor
+     o     }
+     o     n: {
+     o         x: (number) x coordinate of the right anchor
+     o         y: (number) y coordinate of the right anchor
+     o     }
+     o     start: {
+     o         x: (number) x coordinate of the start of the curve
+     o         y: (number) y coordinate of the start of the curve
+     o     }
+     o     end: {
+     o         x: (number) x coordinate of the end of the curve
+     o         y: (number) y coordinate of the end of the curve
+     o     }
+     o     alpha: (number) angle of the curve derivative at the point
+     o }
+    \*/
+    R.findDotsAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
+        var t1 = 1 - t,
+            t13 = pow(t1, 3),
+            t12 = pow(t1, 2),
+            t2 = t * t,
+            t3 = t2 * t,
+            x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x,
+            y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y,
+            mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x),
+            my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y),
+            nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x),
+            ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y),
+            ax = t1 * p1x + t * c1x,
+            ay = t1 * p1y + t * c1y,
+            cx = t1 * c2x + t * p2x,
+            cy = t1 * c2y + t * p2y,
+            alpha = (90 - math.atan2(mx - nx, my - ny) * 180 / PI);
+        (mx > nx || my < ny) && (alpha += 180);
+        return {
+            x: x,
+            y: y,
+            m: {x: mx, y: my},
+            n: {x: nx, y: ny},
+            start: {x: ax, y: ay},
+            end: {x: cx, y: cy},
+            alpha: alpha
+        };
+    };
+    /*\
+     * Raphael.bezierBBox
+     [ method ]
+     **
+     * Utility method
+     **
+     * Return bounding box of a given cubic bezier curve
+     > Parameters
+     - p1x (number) x of the first point of the curve
+     - p1y (number) y of the first point of the curve
+     - c1x (number) x of the first anchor of the curve
+     - c1y (number) y of the first anchor of the curve
+     - c2x (number) x of the second anchor of the curve
+     - c2y (number) y of the second anchor of the curve
+     - p2x (number) x of the second point of the curve
+     - p2y (number) y of the second point of the curve
+     * or
+     - bez (array) array of six points for bezier curve
+     = (object) point information in format:
+     o {
+     o     min: {
+     o         x: (number) x coordinate of the left point
+     o         y: (number) y coordinate of the top point
+     o     }
+     o     max: {
+     o         x: (number) x coordinate of the right point
+     o         y: (number) y coordinate of the bottom point
+     o     }
+     o }
+    \*/
+    R.bezierBBox = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
+        if (!R.is(p1x, "array")) {
+            p1x = [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y];
+        }
+        var bbox = curveDim.apply(null, p1x);
+        return {
+            x: bbox.min.x,
+            y: bbox.min.y,
+            x2: bbox.max.x,
+            y2: bbox.max.y,
+            width: bbox.max.x - bbox.min.x,
+            height: bbox.max.y - bbox.min.y
+        };
+    };
+    /*\
+     * Raphael.isPointInsideBBox
+     [ method ]
+     **
+     * Utility method
+     **
+     * Returns `true` if given point is inside bounding boxes.
+     > Parameters
+     - bbox (string) bounding box
+     - x (string) x coordinate of the point
+     - y (string) y coordinate of the point
+     = (boolean) `true` if point inside
+    \*/
+    R.isPointInsideBBox = function (bbox, x, y) {
+        return x >= bbox.x && x <= bbox.x2 && y >= bbox.y && y <= bbox.y2;
+    };
+    /*\
+     * Raphael.isBBoxIntersect
+     [ method ]
+     **
+     * Utility method
+     **
+     * Returns `true` if two bounding boxes intersect
+     > Parameters
+     - bbox1 (string) first bounding box
+     - bbox2 (string) second bounding box
+     = (boolean) `true` if they intersect
+    \*/
+    R.isBBoxIntersect = function (bbox1, bbox2) {
+        var i = R.isPointInsideBBox;
+        return i(bbox2, bbox1.x, bbox1.y)
+            || i(bbox2, bbox1.x2, bbox1.y)
+            || i(bbox2, bbox1.x, bbox1.y2)
+            || i(bbox2, bbox1.x2, bbox1.y2)
+            || i(bbox1, bbox2.x, bbox2.y)
+            || i(bbox1, bbox2.x2, bbox2.y)
+            || i(bbox1, bbox2.x, bbox2.y2)
+            || i(bbox1, bbox2.x2, bbox2.y2)
+            || (bbox1.x < bbox2.x2 && bbox1.x > bbox2.x || bbox2.x < bbox1.x2 && bbox2.x > bbox1.x)
+            && (bbox1.y < bbox2.y2 && bbox1.y > bbox2.y || bbox2.y < bbox1.y2 && bbox2.y > bbox1.y);
+    };
+    function base3(t, p1, p2, p3, p4) {
+        var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4,
+            t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3;
+        return t * t2 - 3 * p1 + 3 * p2;
+    }
+    function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) {
+        if (z == null) {
+            z = 1;
+        }
+        z = z > 1 ? 1 : z < 0 ? 0 : z;
+        var z2 = z / 2,
+            n = 12,
+            Tvalues = [-0.1252,0.1252,-0.3678,0.3678,-0.5873,0.5873,-0.7699,0.7699,-0.9041,0.9041,-0.9816,0.9816],
+            Cvalues = [0.2491,0.2491,0.2335,0.2335,0.2032,0.2032,0.1601,0.1601,0.1069,0.1069,0.0472,0.0472],
+            sum = 0;
+        for (var i = 0; i < n; i++) {
+            var ct = z2 * Tvalues[i] + z2,
+                xbase = base3(ct, x1, x2, x3, x4),
+                ybase = base3(ct, y1, y2, y3, y4),
+                comb = xbase * xbase + ybase * ybase;
+            sum += Cvalues[i] * math.sqrt(comb);
+        }
+        return z2 * sum;
+    }
+    function getTatLen(x1, y1, x2, y2, x3, y3, x4, y4, ll) {
+        if (ll < 0 || bezlen(x1, y1, x2, y2, x3, y3, x4, y4) < ll) {
+            return;
+        }
+        var t = 1,
+            step = t / 2,
+            t2 = t - step,
+            l,
+            e = .01;
+        l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2);
+        while (abs(l - ll) > e) {
+            step /= 2;
+            t2 += (l < ll ? 1 : -1) * step;
+            l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2);
+        }
+        return t2;
+    }
+    function intersect(x1, y1, x2, y2, x3, y3, x4, y4) {
+        if (
+            mmax(x1, x2) < mmin(x3, x4) ||
+            mmin(x1, x2) > mmax(x3, x4) ||
+            mmax(y1, y2) < mmin(y3, y4) ||
+            mmin(y1, y2) > mmax(y3, y4)
+        ) {
+            return;
+        }
+        var nx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4),
+            ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4),
+            denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
+
+        if (!denominator) {
+            return;
+        }
+        var px = nx / denominator,
+            py = ny / denominator,
+            px2 = +px.toFixed(2),
+            py2 = +py.toFixed(2);
+        if (
+            px2 < +mmin(x1, x2).toFixed(2) ||
+            px2 > +mmax(x1, x2).toFixed(2) ||
+            px2 < +mmin(x3, x4).toFixed(2) ||
+            px2 > +mmax(x3, x4).toFixed(2) ||
+            py2 < +mmin(y1, y2).toFixed(2) ||
+            py2 > +mmax(y1, y2).toFixed(2) ||
+            py2 < +mmin(y3, y4).toFixed(2) ||
+            py2 > +mmax(y3, y4).toFixed(2)
+        ) {
+            return;
+        }
+        return {x: px, y: py};
+    }
+    function inter(bez1, bez2) {
+        return interHelper(bez1, bez2);
+    }
+    function interCount(bez1, bez2) {
+        return interHelper(bez1, bez2, 1);
+    }
+    function interHelper(bez1, bez2, justCount) {
+        var bbox1 = R.bezierBBox(bez1),
+            bbox2 = R.bezierBBox(bez2);
+        if (!R.isBBoxIntersect(bbox1, bbox2)) {
+            return justCount ? 0 : [];
+        }
+        var l1 = bezlen.apply(0, bez1),
+            l2 = bezlen.apply(0, bez2),
+            n1 = mmax(~~(l1 / 5), 1),
+            n2 = mmax(~~(l2 / 5), 1),
+            dots1 = [],
+            dots2 = [],
+            xy = {},
+            res = justCount ? 0 : [];
+        for (var i = 0; i < n1 + 1; i++) {
+            var p = R.findDotsAtSegment.apply(R, bez1.concat(i / n1));
+            dots1.push({x: p.x, y: p.y, t: i / n1});
+        }
+        for (i = 0; i < n2 + 1; i++) {
+            p = R.findDotsAtSegment.apply(R, bez2.concat(i / n2));
+            dots2.push({x: p.x, y: p.y, t: i / n2});
+        }
+        for (i = 0; i < n1; i++) {
+            for (var j = 0; j < n2; j++) {
+                var di = dots1[i],
+                    di1 = dots1[i + 1],
+                    dj = dots2[j],
+                    dj1 = dots2[j + 1],
+                    ci = abs(di1.x - di.x) < .001 ? "y" : "x",
+                    cj = abs(dj1.x - dj.x) < .001 ? "y" : "x",
+                    is = intersect(di.x, di.y, di1.x, di1.y, dj.x, dj.y, dj1.x, dj1.y);
+                if (is) {
+                    if (xy[is.x.toFixed(4)] == is.y.toFixed(4)) {
+                        continue;
+                    }
+                    xy[is.x.toFixed(4)] = is.y.toFixed(4);
+                    var t1 = di.t + abs((is[ci] - di[ci]) / (di1[ci] - di[ci])) * (di1.t - di.t),
+                        t2 = dj.t + abs((is[cj] - dj[cj]) / (dj1[cj] - dj[cj])) * (dj1.t - dj.t);
+                    if (t1 >= 0 && t1 <= 1.001 && t2 >= 0 && t2 <= 1.001) {
+                        if (justCount) {
+                            res++;
+                        } else {
+                            res.push({
+                                x: is.x,
+                                y: is.y,
+                                t1: mmin(t1, 1),
+                                t2: mmin(t2, 1)
+                            });
+                        }
+                    }
+                }
+            }
+        }
+        return res;
+    }
+    /*\
+     * Raphael.pathIntersection
+     [ method ]
+     **
+     * Utility method
+     **
+     * Finds intersections of two paths
+     > Parameters
+     - path1 (string) path string
+     - path2 (string) path string
+     = (array) dots of intersection
+     o [
+     o     {
+     o         x: (number) x coordinate of the point
+     o         y: (number) y coordinate of the point
+     o         t1: (number) t value for segment of path1
+     o         t2: (number) t value for segment of path2
+     o         segment1: (number) order number for segment of path1
+     o         segment2: (number) order number for segment of path2
+     o         bez1: (array) eight coordinates representing beziér curve for the segment of path1
+     o         bez2: (array) eight coordinates representing beziér curve for the segment of path2
+     o     }
+     o ]
+    \*/
+    R.pathIntersection = function (path1, path2) {
+        return interPathHelper(path1, path2);
+    };
+    R.pathIntersectionNumber = function (path1, path2) {
+        return interPathHelper(path1, path2, 1);
+    };
+    function interPathHelper(path1, path2, justCount) {
+        path1 = R._path2curve(path1);
+        path2 = R._path2curve(path2);
+        var x1, y1, x2, y2, x1m, y1m, x2m, y2m, bez1, bez2,
+            res = justCount ? 0 : [];
+        for (var i = 0, ii = path1.length; i < ii; i++) {
+            var pi = path1[i];
+            if (pi[0] == "M") {
+                x1 = x1m = pi[1];
+                y1 = y1m = pi[2];
+            } else {
+                if (pi[0] == "C") {
+                    bez1 = [x1, y1].concat(pi.slice(1));
+                    x1 = bez1[6];
+                    y1 = bez1[7];
+                } else {
+                    bez1 = [x1, y1, x1, y1, x1m, y1m, x1m, y1m];
+                    x1 = x1m;
+                    y1 = y1m;
+                }
+                for (var j = 0, jj = path2.length; j < jj; j++) {
+                    var pj = path2[j];
+                    if (pj[0] == "M") {
+                        x2 = x2m = pj[1];
+                        y2 = y2m = pj[2];
+                    } else {
+                        if (pj[0] == "C") {
+                            bez2 = [x2, y2].concat(pj.slice(1));
+                            x2 = bez2[6];
+                            y2 = bez2[7];
+                        } else {
+                            bez2 = [x2, y2, x2, y2, x2m, y2m, x2m, y2m];
+                            x2 = x2m;
+                            y2 = y2m;
+                        }
+                        var intr = interHelper(bez1, bez2, justCount);
+                        if (justCount) {
+                            res += intr;
+                        } else {
+                            for (var k = 0, kk = intr.length; k < kk; k++) {
+                                intr[k].segment1 = i;
+                                intr[k].segment2 = j;
+                                intr[k].bez1 = bez1;
+                                intr[k].bez2 = bez2;
+                            }
+                            res = res.concat(intr);
+                        }
+                    }
+                }
+            }
+        }
+        return res;
+    }
+    /*\
+     * Raphael.isPointInsidePath
+     [ method ]
+     **
+     * Utility method
+     **
+     * Returns `true` if given point is inside a given closed path.
+     > Parameters
+     - path (string) path string
+     - x (number) x of the point
+     - y (number) y of the point
+     = (boolean) true, if point is inside the path
+    \*/
+    R.isPointInsidePath = function (path, x, y) {
+        var bbox = R.pathBBox(path);
+        return R.isPointInsideBBox(bbox, x, y) &&
+               interPathHelper(path, [["M", x, y], ["H", bbox.x2 + 10]], 1) % 2 == 1;
+    };
+    R._removedFactory = function (methodname) {
+        return function () {
+            eve("raphael.log", null, "Rapha\xebl: you are calling to method \u201c" + methodname + "\u201d of removed object", methodname);
+        };
+    };
+    /*\
+     * Raphael.pathBBox
+     [ method ]
+     **
+     * Utility method
+     **
+     * Return bounding box of a given path
+     > Parameters
+     - path (string) path string
+     = (object) bounding box
+     o {
+     o     x: (number) x coordinate of the left top point of the box
+     o     y: (number) y coordinate of the left top point of the box
+     o     x2: (number) x coordinate of the right bottom point of the box
+     o     y2: (number) y coordinate of the right bottom point of the box
+     o     width: (number) width of the box
+     o     height: (number) height of the box
+     o     cx: (number) x coordinate of the center of the box
+     o     cy: (number) y coordinate of the center of the box
+     o }
+    \*/
+    var pathDimensions = R.pathBBox = function (path) {
+        var pth = paths(path);
+        if (pth.bbox) {
+            return clone(pth.bbox);
+        }
+        if (!path) {
+            return {x: 0, y: 0, width: 0, height: 0, x2: 0, y2: 0};
+        }
+        path = path2curve(path);
+        var x = 0,
+            y = 0,
+            X = [],
+            Y = [],
+            p;
+        for (var i = 0, ii = path.length; i < ii; i++) {
+            p = path[i];
+            if (p[0] == "M") {
+                x = p[1];
+                y = p[2];
+                X.push(x);
+                Y.push(y);
+            } else {
+                var dim = curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
+                X = X[concat](dim.min.x, dim.max.x);
+                Y = Y[concat](dim.min.y, dim.max.y);
+                x = p[5];
+                y = p[6];
+            }
+        }
+        var xmin = mmin[apply](0, X),
+            ymin = mmin[apply](0, Y),
+            xmax = mmax[apply](0, X),
+            ymax = mmax[apply](0, Y),
+            width = xmax - xmin,
+            height = ymax - ymin,
+                bb = {
+                x: xmin,
+                y: ymin,
+                x2: xmax,
+                y2: ymax,
+                width: width,
+                height: height,
+                cx: xmin + width / 2,
+                cy: ymin + height / 2
+            };
+        pth.bbox = clone(bb);
+        return bb;
+    },
+        pathClone = function (pathArray) {
+            var res = clone(pathArray);
+            res.toString = R._path2string;
+            return res;
+        },
+        pathToRelative = R._pathToRelative = function (pathArray) {
+            var pth = paths(pathArray);
+            if (pth.rel) {
+                return pathClone(pth.rel);
+            }
+            if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
+                pathArray = R.parsePathString(pathArray);
+            }
+            var res = [],
+                x = 0,
+                y = 0,
+                mx = 0,
+                my = 0,
+                start = 0;
+            if (pathArray[0][0] == "M") {
+                x = pathArray[0][1];
+                y = pathArray[0][2];
+                mx = x;
+                my = y;
+                start++;
+                res.push(["M", x, y]);
+            }
+            for (var i = start, ii = pathArray.length; i < ii; i++) {
+                var r = res[i] = [],
+                    pa = pathArray[i];
+                if (pa[0] != lowerCase.call(pa[0])) {
+                    r[0] = lowerCase.call(pa[0]);
+                    switch (r[0]) {
+                        case "a":
+                            r[1] = pa[1];
+                            r[2] = pa[2];
+                            r[3] = pa[3];
+                            r[4] = pa[4];
+                            r[5] = pa[5];
+                            r[6] = +(pa[6] - x).toFixed(3);
+                            r[7] = +(pa[7] - y).toFixed(3);
+                            break;
+                        case "v":
+                            r[1] = +(pa[1] - y).toFixed(3);
+                            break;
+                        case "m":
+                            mx = pa[1];
+                            my = pa[2];
+                        default:
+                            for (var j = 1, jj = pa.length; j < jj; j++) {
+                                r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
+                            }
+                    }
+                } else {
+                    r = res[i] = [];
+                    if (pa[0] == "m") {
+                        mx = pa[1] + x;
+                        my = pa[2] + y;
+                    }
+                    for (var k = 0, kk = pa.length; k < kk; k++) {
+                        res[i][k] = pa[k];
+                    }
+                }
+                var len = res[i].length;
+                switch (res[i][0]) {
+                    case "z":
+                        x = mx;
+                        y = my;
+                        break;
+                    case "h":
+                        x += +res[i][len - 1];
+                        break;
+                    case "v":
+                        y += +res[i][len - 1];
+                        break;
+                    default:
+                        x += +res[i][len - 2];
+                        y += +res[i][len - 1];
+                }
+            }
+            res.toString = R._path2string;
+            pth.rel = pathClone(res);
+            return res;
+        },
+        pathToAbsolute = R._pathToAbsolute = function (pathArray) {
+            var pth = paths(pathArray);
+            if (pth.abs) {
+                return pathClone(pth.abs);
+            }
+            if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
+                pathArray = R.parsePathString(pathArray);
+            }
+            if (!pathArray || !pathArray.length) {
+                return [["M", 0, 0]];
+            }
+            var res = [],
+                x = 0,
+                y = 0,
+                mx = 0,
+                my = 0,
+                start = 0;
+            if (pathArray[0][0] == "M") {
+                x = +pathArray[0][1];
+                y = +pathArray[0][2];
+                mx = x;
+                my = y;
+                start++;
+                res[0] = ["M", x, y];
+            }
+            var crz = pathArray.length == 3 && pathArray[0][0] == "M" && pathArray[1][0].toUpperCase() == "R" && pathArray[2][0].toUpperCase() == "Z";
+            for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) {
+                res.push(r = []);
+                pa = pathArray[i];
+                if (pa[0] != upperCase.call(pa[0])) {
+                    r[0] = upperCase.call(pa[0]);
+                    switch (r[0]) {
+                        case "A":
+                            r[1] = pa[1];
+                            r[2] = pa[2];
+                            r[3] = pa[3];
+                            r[4] = pa[4];
+                            r[5] = pa[5];
+                            r[6] = +(pa[6] + x);
+                            r[7] = +(pa[7] + y);
+                            break;
+                        case "V":
+                            r[1] = +pa[1] + y;
+                            break;
+                        case "H":
+                            r[1] = +pa[1] + x;
+                            break;
+                        case "R":
+                            var dots = [x, y][concat](pa.slice(1));
+                            for (var j = 2, jj = dots.length; j < jj; j++) {
+                                dots[j] = +dots[j] + x;
+                                dots[++j] = +dots[j] + y;
+                            }
+                            res.pop();
+                            res = res[concat](catmullRom2bezier(dots, crz));
+                            break;
+                        case "M":
+                            mx = +pa[1] + x;
+                            my = +pa[2] + y;
+                        default:
+                            for (j = 1, jj = pa.length; j < jj; j++) {
+                                r[j] = +pa[j] + ((j % 2) ? x : y);
+                            }
+                    }
+                } else if (pa[0] == "R") {
+                    dots = [x, y][concat](pa.slice(1));
+                    res.pop();
+                    res = res[concat](catmullRom2bezier(dots, crz));
+                    r = ["R"][concat](pa.slice(-2));
+                } else {
+                    for (var k = 0, kk = pa.length; k < kk; k++) {
+                        r[k] = pa[k];
+                    }
+                }
+                switch (r[0]) {
+                    case "Z":
+                        x = mx;
+                        y = my;
+                        break;
+                    case "H":
+                        x = r[1];
+                        break;
+                    case "V":
+                        y = r[1];
+                        break;
+                    case "M":
+                        mx = r[r.length - 2];
+                        my = r[r.length - 1];
+                    default:
+                        x = r[r.length - 2];
+                        y = r[r.length - 1];
+                }
+            }
+            res.toString = R._path2string;
+            pth.abs = pathClone(res);
+            return res;
+        },
+        l2c = function (x1, y1, x2, y2) {
+            return [x1, y1, x2, y2, x2, y2];
+        },
+        q2c = function (x1, y1, ax, ay, x2, y2) {
+            var _13 = 1 / 3,
+                _23 = 2 / 3;
+            return [
+                    _13 * x1 + _23 * ax,
+                    _13 * y1 + _23 * ay,
+                    _13 * x2 + _23 * ax,
+                    _13 * y2 + _23 * ay,
+                    x2,
+                    y2
+                ];
+        },
+        a2c = function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
+            // for more information of where this math came from visit:
+            // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
+            var _120 = PI * 120 / 180,
+                rad = PI / 180 * (+angle || 0),
+                res = [],
+                xy,
+                rotate = cacher(function (x, y, rad) {
+                    var X = x * math.cos(rad) - y * math.sin(rad),
+                        Y = x * math.sin(rad) + y * math.cos(rad);
+                    return {x: X, y: Y};
+                });
+            if (!recursive) {
+                xy = rotate(x1, y1, -rad);
+                x1 = xy.x;
+                y1 = xy.y;
+                xy = rotate(x2, y2, -rad);
+                x2 = xy.x;
+                y2 = xy.y;
+                var cos = math.cos(PI / 180 * angle),
+                    sin = math.sin(PI / 180 * angle),
+                    x = (x1 - x2) / 2,
+                    y = (y1 - y2) / 2;
+                var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
+                if (h > 1) {
+                    h = math.sqrt(h);
+                    rx = h * rx;
+                    ry = h * ry;
+                }
+                var rx2 = rx * rx,
+                    ry2 = ry * ry,
+                    k = (large_arc_flag == sweep_flag ? -1 : 1) *
+                        math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
+                    cx = k * rx * y / ry + (x1 + x2) / 2,
+                    cy = k * -ry * x / rx + (y1 + y2) / 2,
+                    f1 = math.asin(((y1 - cy) / ry).toFixed(9)),
+                    f2 = math.asin(((y2 - cy) / ry).toFixed(9));
+
+                f1 = x1 < cx ? PI - f1 : f1;
+                f2 = x2 < cx ? PI - f2 : f2;
+                f1 < 0 && (f1 = PI * 2 + f1);
+                f2 < 0 && (f2 = PI * 2 + f2);
+                if (sweep_flag && f1 > f2) {
+                    f1 = f1 - PI * 2;
+                }
+                if (!sweep_flag && f2 > f1) {
+                    f2 = f2 - PI * 2;
+                }
+            } else {
+                f1 = recursive[0];
+                f2 = recursive[1];
+                cx = recursive[2];
+                cy = recursive[3];
+            }
+            var df = f2 - f1;
+            if (abs(df) > _120) {
+                var f2old = f2,
+                    x2old = x2,
+                    y2old = y2;
+                f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
+                x2 = cx + rx * math.cos(f2);
+                y2 = cy + ry * math.sin(f2);
+                res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
+            }
+            df = f2 - f1;
+            var c1 = math.cos(f1),
+                s1 = math.sin(f1),
+                c2 = math.cos(f2),
+                s2 = math.sin(f2),
+                t = math.tan(df / 4),
+                hx = 4 / 3 * rx * t,
+                hy = 4 / 3 * ry * t,
+                m1 = [x1, y1],
+                m2 = [x1 + hx * s1, y1 - hy * c1],
+                m3 = [x2 + hx * s2, y2 - hy * c2],
+                m4 = [x2, y2];
+            m2[0] = 2 * m1[0] - m2[0];
+            m2[1] = 2 * m1[1] - m2[1];
+            if (recursive) {
+                return [m2, m3, m4][concat](res);
+            } else {
+                res = [m2, m3, m4][concat](res).join()[split](",");
+                var newres = [];
+                for (var i = 0, ii = res.length; i < ii; i++) {
+                    newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
+                }
+                return newres;
+            }
+        },
+        findDotAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
+            var t1 = 1 - t;
+            return {
+                x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x,
+                y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y
+            };
+        },
+        curveDim = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
+            var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
+                b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
+                c = p1x - c1x,
+                t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a,
+                t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a,
+                y = [p1y, p2y],
+                x = [p1x, p2x],
+                dot;
+            abs(t1) > "1e12" && (t1 = .5);
+            abs(t2) > "1e12" && (t2 = .5);
+            if (t1 > 0 && t1 < 1) {
+                dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
+                x.push(dot.x);
+                y.push(dot.y);
+            }
+            if (t2 > 0 && t2 < 1) {
+                dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
+                x.push(dot.x);
+                y.push(dot.y);
+            }
+            a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
+            b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
+            c = p1y - c1y;
+            t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a;
+            t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a;
+            abs(t1) > "1e12" && (t1 = .5);
+            abs(t2) > "1e12" && (t2 = .5);
+            if (t1 > 0 && t1 < 1) {
+                dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
+                x.push(dot.x);
+                y.push(dot.y);
+            }
+            if (t2 > 0 && t2 < 1) {
+                dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
+                x.push(dot.x);
+                y.push(dot.y);
+            }
+            return {
+                min: {x: mmin[apply](0, x), y: mmin[apply](0, y)},
+                max: {x: mmax[apply](0, x), y: mmax[apply](0, y)}
+            };
+        }),
+        path2curve = R._path2curve = cacher(function (path, path2) {
+            var pth = !path2 && paths(path);
+            if (!path2 && pth.curve) {
+                return pathClone(pth.curve);
+            }
+            var p = pathToAbsolute(path),
+                p2 = path2 && pathToAbsolute(path2),
+                attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
+                attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
+                processPath = function (path, d, pcom) {
+                    var nx, ny, tq = {T:1, Q:1};
+                    if (!path) {
+                        return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
+                    }
+                    !(path[0] in tq) && (d.qx = d.qy = null);
+                    switch (path[0]) {
+                        case "M":
+                            d.X = path[1];
+                            d.Y = path[2];
+                            break;
+                        case "A":
+                            path = ["C"][concat](a2c[apply](0, [d.x, d.y][concat](path.slice(1))));
+                            break;
+                        case "S":
+                            if (pcom == "C" || pcom == "S") { // In "S" case we have to take into account, if the previous command is C/S.
+                                nx = d.x * 2 - d.bx;          // And reflect the previous
+                                ny = d.y * 2 - d.by;          // command's control point relative to the current point.
+                            }
+                            else {                            // or some else or nothing
+                                nx = d.x;
+                                ny = d.y;
+                            }
+                            path = ["C", nx, ny][concat](path.slice(1));
+                            break;
+                        case "T":
+                            if (pcom == "Q" || pcom == "T") { // In "T" case we have to take into account, if the previous command is Q/T.
+                                d.qx = d.x * 2 - d.qx;        // And make a reflection similar
+                                d.qy = d.y * 2 - d.qy;        // to case "S".
+                            }
+                            else {                            // or something else or nothing
+                                d.qx = d.x;
+                                d.qy = d.y;
+                            }
+                            path = ["C"][concat](q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]));
+                            break;
+                        case "Q":
+                            d.qx = path[1];
+                            d.qy = path[2];
+                            path = ["C"][concat](q2c(d.x, d.y, path[1], path[2], path[3], path[4]));
+                            break;
+                        case "L":
+                            path = ["C"][concat](l2c(d.x, d.y, path[1], path[2]));
+                            break;
+                        case "H":
+                            path = ["C"][concat](l2c(d.x, d.y, path[1], d.y));
+                            break;
+                        case "V":
+                            path = ["C"][concat](l2c(d.x, d.y, d.x, path[1]));
+                            break;
+                        case "Z":
+                            path = ["C"][concat](l2c(d.x, d.y, d.X, d.Y));
+                            break;
+                    }
+                    return path;
+                },
+                fixArc = function (pp, i) {
+                    if (pp[i].length > 7) {
+                        pp[i].shift();
+                        var pi = pp[i];
+                        while (pi.length) {
+                            pcoms1[i]="A"; // if created multiple C:s, their original seg is saved
+                            p2 && (pcoms2[i]="A"); // the same as above
+                            pp.splice(i++, 0, ["C"][concat](pi.splice(0, 6)));
+                        }
+                        pp.splice(i, 1);
+                        ii = mmax(p.length, p2 && p2.length || 0);
+                    }
+                },
+                fixM = function (path1, path2, a1, a2, i) {
+                    if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
+                        path2.splice(i, 0, ["M", a2.x, a2.y]);
+                        a1.bx = 0;
+                        a1.by = 0;
+                        a1.x = path1[i][1];
+                        a1.y = path1[i][2];
+                        ii = mmax(p.length, p2 && p2.length || 0);
+                    }
+                },
+                pcoms1 = [], // path commands of original path p
+                pcoms2 = [], // path commands of original path p2
+                pfirst = "", // temporary holder for original path command
+                pcom = ""; // holder for previous path command of original path
+            for (var i = 0, ii = mmax(p.length, p2 && p2.length || 0); i < ii; i++) {
+                p[i] && (pfirst = p[i][0]); // save current path command
+
+                if (pfirst != "C") // C is not saved yet, because it may be result of conversion
+                {
+                    pcoms1[i] = pfirst; // Save current path command
+                    i && ( pcom = pcoms1[i-1]); // Get previous path command pcom
+                }
+                p[i] = processPath(p[i], attrs, pcom); // Previous path command is inputted to processPath
+
+                if (pcoms1[i] != "A" && pfirst == "C") pcoms1[i] = "C"; // A is the only command
+                // which may produce multiple C:s
+                // so we have to make sure that C is also C in original path
+
+                fixArc(p, i); // fixArc adds also the right amount of A:s to pcoms1
+
+                if (p2) { // the same procedures is done to p2
+                    p2[i] && (pfirst = p2[i][0]);
+                    if (pfirst != "C")
+                    {
+                        pcoms2[i] = pfirst;
+                        i && (pcom = pcoms2[i-1]);
+                    }
+                    p2[i] = processPath(p2[i], attrs2, pcom);
+
+                    if (pcoms2[i]!="A" && pfirst=="C") pcoms2[i]="C";
+
+                    fixArc(p2, i);
+                }
+                fixM(p, p2, attrs, attrs2, i);
+                fixM(p2, p, attrs2, attrs, i);
+                var seg = p[i],
+                    seg2 = p2 && p2[i],
+                    seglen = seg.length,
+                    seg2len = p2 && seg2.length;
+                attrs.x = seg[seglen - 2];
+                attrs.y = seg[seglen - 1];
+                attrs.bx = toFloat(seg[seglen - 4]) || attrs.x;
+                attrs.by = toFloat(seg[seglen - 3]) || attrs.y;
+                attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x);
+                attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y);
+                attrs2.x = p2 && seg2[seg2len - 2];
+                attrs2.y = p2 && seg2[seg2len - 1];
+            }
+            if (!p2) {
+                pth.curve = pathClone(p);
+            }
+            return p2 ? [p, p2] : p;
+        }, null, pathClone),
+        parseDots = R._parseDots = cacher(function (gradient) {
+            var dots = [];
+            for (var i = 0, ii = gradient.length; i < ii; i++) {
+                var dot = {},
+                    par = gradient[i].match(/^([^:]*):?([\d\.]*)/);
+                dot.color = R.getRGB(par[1]);
+                if (dot.color.error) {
+                    return null;
+                }
+                dot.color = dot.color.hex;
+                par[2] && (dot.offset = par[2] + "%");
+                dots.push(dot);
+            }
+            for (i = 1, ii = dots.length - 1; i < ii; i++) {
+                if (!dots[i].offset) {
+                    var start = toFloat(dots[i - 1].offset || 0),
+                        end = 0;
+                    for (var j = i + 1; j < ii; j++) {
+                        if (dots[j].offset) {
+                            end = dots[j].offset;
+                            break;
+                        }
+                    }
+                    if (!end) {
+                        end = 100;
+                        j = ii;
+                    }
+                    end = toFloat(end);
+                    var d = (end - start) / (j - i + 1);
+                    for (; i < j; i++) {
+                        start += d;
+                        dots[i].offset = start + "%";
+                    }
+                }
+            }
+            return dots;
+        }),
+        tear = R._tear = function (el, paper) {
+            el == paper.top && (paper.top = el.prev);
+            el == paper.bottom && (paper.bottom = el.next);
+            el.next && (el.next.prev = el.prev);
+            el.prev && (el.prev.next = el.next);
+        },
+        tofront = R._tofront = function (el, paper) {
+            if (paper.top === el) {
+                return;
+            }
+            tear(el, paper);
+            el.next = null;
+            el.prev = paper.top;
+            paper.top.next = el;
+            paper.top = el;
+        },
+        toback = R._toback = function (el, paper) {
+            if (paper.bottom === el) {
+                return;
+            }
+            tear(el, paper);
+            el.next = paper.bottom;
+            el.prev = null;
+            paper.bottom.prev = el;
+            paper.bottom = el;
+        },
+        insertafter = R._insertafter = function (el, el2, paper) {
+            tear(el, paper);
+            el2 == paper.top && (paper.top = el);
+            el2.next && (el2.next.prev = el);
+            el.next = el2.next;
+            el.prev = el2;
+            el2.next = el;
+        },
+        insertbefore = R._insertbefore = function (el, el2, paper) {
+            tear(el, paper);
+            el2 == paper.bottom && (paper.bottom = el);
+            el2.prev && (el2.prev.next = el);
+            el.prev = el2.prev;
+            el2.prev = el;
+            el.next = el2;
+        },
+        /*\
+         * Raphael.toMatrix
+         [ method ]
+         **
+         * Utility method
+         **
+         * Returns matrix of transformations applied to a given path
+         > Parameters
+         - path (string) path string
+         - transform (string|array) transformation string
+         = (object) @Matrix
+        \*/
+        toMatrix = R.toMatrix = function (path, transform) {
+            var bb = pathDimensions(path),
+                el = {
+                    _: {
+                        transform: E
+                    },
+                    getBBox: function () {
+                        return bb;
+                    }
+                };
+            extractTransform(el, transform);
+            return el.matrix;
+        },
+        /*\
+         * Raphael.transformPath
+         [ method ]
+         **
+         * Utility method
+         **
+         * Returns path transformed by a given transformation
+         > Parameters
+         - path (string) path string
+         - transform (string|array) transformation string
+         = (string) path
+        \*/
+        transformPath = R.transformPath = function (path, transform) {
+            return mapPath(path, toMatrix(path, transform));
+        },
+        extractTransform = R._extractTransform = function (el, tstr) {
+            if (tstr == null) {
+                return el._.transform;
+            }
+            tstr = Str(tstr).replace(/\.{3}|\u2026/g, el._.transform || E);
+            var tdata = R.parseTransformString(tstr),
+                deg = 0,
+                dx = 0,
+                dy = 0,
+                sx = 1,
+                sy = 1,
+                _ = el._,
+                m = new Matrix;
+            _.transform = tdata || [];
+            if (tdata) {
+                for (var i = 0, ii = tdata.length; i < ii; i++) {
+                    var t = tdata[i],
+                        tlen = t.length,
+                        command = Str(t[0]).toLowerCase(),
+                        absolute = t[0] != command,
+                        inver = absolute ? m.invert() : 0,
+                        x1,
+                        y1,
+                        x2,
+                        y2,
+                        bb;
+                    if (command == "t" && tlen == 3) {
+                        if (absolute) {
+                            x1 = inver.x(0, 0);
+                            y1 = inver.y(0, 0);
+                            x2 = inver.x(t[1], t[2]);
+                            y2 = inver.y(t[1], t[2]);
+                            m.translate(x2 - x1, y2 - y1);
+                        } else {
+                            m.translate(t[1], t[2]);
+                        }
+                    } else if (command == "r") {
+                        if (tlen == 2) {
+                            bb = bb || el.getBBox(1);
+                            m.rotate(t[1], bb.x + bb.width / 2, bb.y + bb.height / 2);
+                            deg += t[1];
+                        } else if (tlen == 4) {
+                            if (absolute) {
+                                x2 = inver.x(t[2], t[3]);
+                                y2 = inver.y(t[2], t[3]);
+                                m.rotate(t[1], x2, y2);
+                            } else {
+                                m.rotate(t[1], t[2], t[3]);
+                            }
+                            deg += t[1];
+                        }
+                    } else if (command == "s") {
+                        if (tlen == 2 || tlen == 3) {
+                            bb = bb || el.getBBox(1);
+                            m.scale(t[1], t[tlen - 1], bb.x + bb.width / 2, bb.y + bb.height / 2);
+                            sx *= t[1];
+                            sy *= t[tlen - 1];
+                        } else if (tlen == 5) {
+                            if (absolute) {
+                                x2 = inver.x(t[3], t[4]);
+                                y2 = inver.y(t[3], t[4]);
+                                m.scale(t[1], t[2], x2, y2);
+                            } else {
+                                m.scale(t[1], t[2], t[3], t[4]);
+                            }
+                            sx *= t[1];
+                            sy *= t[2];
+                        }
+                    } else if (command == "m" && tlen == 7) {
+                        m.add(t[1], t[2], t[3], t[4], t[5], t[6]);
+                    }
+                    _.dirtyT = 1;
+                    el.matrix = m;
+                }
+            }
+
+            /*\
+             * Element.matrix
+             [ property (object) ]
+             **
+             * Keeps @Matrix object, which represents element transformation
+            \*/
+            el.matrix = m;
+
+            _.sx = sx;
+            _.sy = sy;
+            _.deg = deg;
+            _.dx = dx = m.e;
+            _.dy = dy = m.f;
+
+            if (sx == 1 && sy == 1 && !deg && _.bbox) {
+                _.bbox.x += +dx;
+                _.bbox.y += +dy;
+            } else {
+                _.dirtyT = 1;
+            }
+        },
+        getEmpty = function (item) {
+            var l = item[0];
+            switch (l.toLowerCase()) {
+                case "t": return [l, 0, 0];
+                case "m": return [l, 1, 0, 0, 1, 0, 0];
+                case "r": if (item.length == 4) {
+                    return [l, 0, item[2], item[3]];
+                } else {
+                    return [l, 0];
+                }
+                case "s": if (item.length == 5) {
+                    return [l, 1, 1, item[3], item[4]];
+                } else if (item.length == 3) {
+                    return [l, 1, 1];
+                } else {
+                    return [l, 1];
+                }
+            }
+        },
+        equaliseTransform = R._equaliseTransform = function (t1, t2) {
+            t2 = Str(t2).replace(/\.{3}|\u2026/g, t1);
+            t1 = R.parseTransformString(t1) || [];
+            t2 = R.parseTransformString(t2) || [];
+            var maxlength = mmax(t1.length, t2.length),
+                from = [],
+                to = [],
+                i = 0, j, jj,
+                tt1, tt2;
+            for (; i < maxlength; i++) {
+                tt1 = t1[i] || getEmpty(t2[i]);
+                tt2 = t2[i] || getEmpty(tt1);
+                if ((tt1[0] != tt2[0]) ||
+                    (tt1[0].toLowerCase() == "r" && (tt1[2] != tt2[2] || tt1[3] != tt2[3])) ||
+                    (tt1[0].toLowerCase() == "s" && (tt1[3] != tt2[3] || tt1[4] != tt2[4]))
+                    ) {
+                    return;
+                }
+                from[i] = [];
+                to[i] = [];
+                for (j = 0, jj = mmax(tt1.length, tt2.length); j < jj; j++) {
+                    j in tt1 && (from[i][j] = tt1[j]);
+                    j in tt2 && (to[i][j] = tt2[j]);
+                }
+            }
+            return {
+                from: from,
+                to: to
+            };
+        };
+    R._getContainer = function (x, y, w, h) {
+        var container;
+        container = h == null && !R.is(x, "object") ? g.doc.getElementById(x) : x;
+        if (container == null) {
+            return;
+        }
+        if (container.tagName) {
+            if (y == null) {
+                return {
+                    container: container,
+                    width: container.style.pixelWidth || container.offsetWidth,
+                    height: container.style.pixelHeight || container.offsetHeight
+                };
+            } else {
+                return {
+                    container: container,
+                    width: y,
+                    height: w
+                };
+            }
+        }
+        return {
+            container: 1,
+            x: x,
+            y: y,
+            width: w,
+            height: h
+        };
+    };
+    /*\
+     * Raphael.pathToRelative
+     [ method ]
+     **
+     * Utility method
+     **
+     * Converts path to relative form
+     > Parameters
+     - pathString (string|array) path string or array of segments
+     = (array) array of segments.
+    \*/
+    R.pathToRelative = pathToRelative;
+    R._engine = {};
+    /*\
+     * Raphael.path2curve
+     [ method ]
+     **
+     * Utility method
+     **
+     * Converts path to a new path where all segments are cubic bezier curves.
+     > Parameters
+     - pathString (string|array) path string or array of segments
+     = (array) array of segments.
+    \*/
+    R.path2curve = path2curve;
+    /*\
+     * Raphael.matrix
+     [ method ]
+     **
+     * Utility method
+     **
+     * Returns matrix based on given parameters.
+     > Parameters
+     - a (number)
+     - b (number)
+     - c (number)
+     - d (number)
+     - e (number)
+     - f (number)
+     = (object) @Matrix
+    \*/
+    R.matrix = function (a, b, c, d, e, f) {
+        return new Matrix(a, b, c, d, e, f);
+    };
+    function Matrix(a, b, c, d, e, f) {
+        if (a != null) {
+            this.a = +a;
+            this.b = +b;
+            this.c = +c;
+            this.d = +d;
+            this.e = +e;
+            this.f = +f;
+        } else {
+            this.a = 1;
+            this.b = 0;
+            this.c = 0;
+            this.d = 1;
+            this.e = 0;
+            this.f = 0;
+        }
+    }
+    (function (matrixproto) {
+        /*\
+         * Matrix.add
+         [ method ]
+         **
+         * Adds given matrix to existing one.
+         > Parameters
+         - a (number)
+         - b (number)
+         - c (number)
+         - d (number)
+         - e (number)
+         - f (number)
+         or
+         - matrix (object) @Matrix
+        \*/
+        matrixproto.add = function (a, b, c, d, e, f) {
+            var out = [[], [], []],
+                m = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]],
+                matrix = [[a, c, e], [b, d, f], [0, 0, 1]],
+                x, y, z, res;
+
+            if (a && a instanceof Matrix) {
+                matrix = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]];
+            }
+
+            for (x = 0; x < 3; x++) {
+                for (y = 0; y < 3; y++) {
+                    res = 0;
+                    for (z = 0; z < 3; z++) {
+                        res += m[x][z] * matrix[z][y];
+                    }
+                    out[x][y] = res;
+                }
+            }
+            this.a = out[0][0];
+            this.b = out[1][0];
+            this.c = out[0][1];
+            this.d = out[1][1];
+            this.e = out[0][2];
+            this.f = out[1][2];
+        };
+        /*\
+         * Matrix.invert
+         [ method ]
+         **
+         * Returns inverted version of the matrix
+         = (object) @Matrix
+        \*/
+        matrixproto.invert = function () {
+            var me = this,
+                x = me.a * me.d - me.b * me.c;
+            return new Matrix(me.d / x, -me.b / x, -me.c / x, me.a / x, (me.c * me.f - me.d * me.e) / x, (me.b * me.e - me.a * me.f) / x);
+        };
+        /*\
+         * Matrix.clone
+         [ method ]
+         **
+         * Returns copy of the matrix
+         = (object) @Matrix
+        \*/
+        matrixproto.clone = function () {
+            return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f);
+        };
+        /*\
+         * Matrix.translate
+         [ method ]
+         **
+         * Translate the matrix
+         > Parameters
+         - x (number)
+         - y (number)
+        \*/
+        matrixproto.translate = function (x, y) {
+            this.add(1, 0, 0, 1, x, y);
+        };
+        /*\
+         * Matrix.scale
+         [ method ]
+         **
+         * Scales the matrix
+         > Parameters
+         - x (number)
+         - y (number) #optional
+         - cx (number) #optional
+         - cy (number) #optional
+        \*/
+        matrixproto.scale = function (x, y, cx, cy) {
+            y == null && (y = x);
+            (cx || cy) && this.add(1, 0, 0, 1, cx, cy);
+            this.add(x, 0, 0, y, 0, 0);
+            (cx || cy) && this.add(1, 0, 0, 1, -cx, -cy);
+        };
+        /*\
+         * Matrix.rotate
+         [ method ]
+         **
+         * Rotates the matrix
+         > Parameters
+         - a (number)
+         - x (number)
+         - y (number)
+        \*/
+        matrixproto.rotate = function (a, x, y) {
+            a = R.rad(a);
+            x = x || 0;
+            y = y || 0;
+            var cos = +math.cos(a).toFixed(9),
+                sin = +math.sin(a).toFixed(9);
+            this.add(cos, sin, -sin, cos, x, y);
+            this.add(1, 0, 0, 1, -x, -y);
+        };
+        /*\
+         * Matrix.x
+         [ method ]
+         **
+         * Return x coordinate for given point after transformation described by the matrix. See also @Matrix.y
+         > Parameters
+         - x (number)
+         - y (number)
+         = (number) x
+        \*/
+        matrixproto.x = function (x, y) {
+            return x * this.a + y * this.c + this.e;
+        };
+        /*\
+         * Matrix.y
+         [ method ]
+         **
+         * Return y coordinate for given point after transformation described by the matrix. See also @Matrix.x
+         > Parameters
+         - x (number)
+         - y (number)
+         = (number) y
+        \*/
+        matrixproto.y = function (x, y) {
+            return x * this.b + y * this.d + this.f;
+        };
+        matrixproto.get = function (i) {
+            return +this[Str.fromCharCode(97 + i)].toFixed(4);
+        };
+        matrixproto.toString = function () {
+            return R.svg ?
+                "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")" :
+                [this.get(0), this.get(2), this.get(1), this.get(3), 0, 0].join();
+        };
+        matrixproto.toFilter = function () {
+            return "progid:DXImageTransform.Microsoft.Matrix(M11=" + this.get(0) +
+                ", M12=" + this.get(2) + ", M21=" + this.get(1) + ", M22=" + this.get(3) +
+                ", Dx=" + this.get(4) + ", Dy=" + this.get(5) + ", sizingmethod='auto expand')";
+        };
+        matrixproto.offset = function () {
+            return [this.e.toFixed(4), this.f.toFixed(4)];
+        };
+        function norm(a) {
+            return a[0] * a[0] + a[1] * a[1];
+        }
+        function normalize(a) {
+            var mag = math.sqrt(norm(a));
+            a[0] && (a[0] /= mag);
+            a[1] && (a[1] /= mag);
+        }
+        /*\
+         * Matrix.split
+         [ method ]
+         **
+         * Splits matrix into primitive transformations
+         = (object) in format:
+         o dx (number) translation by x
+         o dy (number) translation by y
+         o scalex (number) scale by x
+         o scaley (number) scale by y
+         o shear (number) shear
+         o rotate (number) rotation in deg
+         o isSimple (boolean) could it be represented via simple transformations
+        \*/
+        matrixproto.split = function () {
+            var out = {};
+            // translation
+            out.dx = this.e;
+            out.dy = this.f;
+
+            // scale and shear
+            var row = [[this.a, this.c], [this.b, this.d]];
+            out.scalex = math.sqrt(norm(row[0]));
+            normalize(row[0]);
+
+            out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1];
+            row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear];
+
+            out.scaley = math.sqrt(norm(row[1]));
+            normalize(row[1]);
+            out.shear /= out.scaley;
+
+            // rotation
+            var sin = -row[0][1],
+                cos = row[1][1];
+            if (cos < 0) {
+                out.rotate = R.deg(math.acos(cos));
+                if (sin < 0) {
+                    out.rotate = 360 - out.rotate;
+                }
+            } else {
+                out.rotate = R.deg(math.asin(sin));
+            }
+
+            out.isSimple = !+out.shear.toFixed(9) && (out.scalex.toFixed(9) == out.scaley.toFixed(9) || !out.rotate);
+            out.isSuperSimple = !+out.shear.toFixed(9) && out.scalex.toFixed(9) == out.scaley.toFixed(9) && !out.rotate;
+            out.noRotation = !+out.shear.toFixed(9) && !out.rotate;
+            return out;
+        };
+        /*\
+         * Matrix.toTransformString
+         [ method ]
+         **
+         * Return transform string that represents given matrix
+         = (string) transform string
+        \*/
+        matrixproto.toTransformString = function (shorter) {
+            var s = shorter || this[split]();
+            if (s.isSimple) {
+                s.scalex = +s.scalex.toFixed(4);
+                s.scaley = +s.scaley.toFixed(4);
+                s.rotate = +s.rotate.toFixed(4);
+                return  (s.dx || s.dy ? "t" + [s.dx, s.dy] : E) +
+                        (s.scalex != 1 || s.scaley != 1 ? "s" + [s.scalex, s.scaley, 0, 0] : E) +
+                        (s.rotate ? "r" + [s.rotate, 0, 0] : E);
+            } else {
+                return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)];
+            }
+        };
+    })(Matrix.prototype);
+
+    // WebKit rendering bug workaround method
+    var version = navigator.userAgent.match(/Version\/(.*?)\s/) || navigator.userAgent.match(/Chrome\/(\d+)/);
+    if ((navigator.vendor == "Apple Computer, Inc.") && (version && version[1] < 4 || navigator.platform.slice(0, 2) == "iP") ||
+        (navigator.vendor == "Google Inc." && version && version[1] < 8)) {
+        /*\
+         * Paper.safari
+         [ method ]
+         **
+         * There is an inconvenient rendering bug in Safari (WebKit):
+         * sometimes the rendering should be forced.
+         * This method should help with dealing with this bug.
+        \*/
+        paperproto.safari = function () {
+            var rect = this.rect(-99, -99, this.width + 99, this.height + 99).attr({stroke: "none"});
+            setTimeout(function () {rect.remove();});
+        };
+    } else {
+        paperproto.safari = fun;
+    }
+
+    var preventDefault = function () {
+        this.returnValue = false;
+    },
+    preventTouch = function () {
+        return this.originalEvent.preventDefault();
+    },
+    stopPropagation = function () {
+        this.cancelBubble = true;
+    },
+    stopTouch = function () {
+        return this.originalEvent.stopPropagation();
+    },
+    getEventPosition = function (e) {
+        var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
+            scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft;
+
+        return {
+            x: e.clientX + scrollX,
+            y: e.clientY + scrollY
+        };
+    },
+    addEvent = (function () {
+        if (g.doc.addEventListener) {
+            return function (obj, type, fn, element) {
+                var f = function (e) {
+                    var pos = getEventPosition(e);
+                    return fn.call(element, e, pos.x, pos.y);
+                };
+                obj.addEventListener(type, f, false);
+
+                if (supportsTouch && touchMap[type]) {
+                    var _f = function (e) {
+                        var pos = getEventPosition(e),
+                            olde = e;
+
+                        for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) {
+                            if (e.targetTouches[i].target == obj) {
+                                e = e.targetTouches[i];
+                                e.originalEvent = olde;
+                                e.preventDefault = preventTouch;
+                                e.stopPropagation = stopTouch;
+                                break;
+                            }
+                        }
+
+                        return fn.call(element, e, pos.x, pos.y);
+                    };
+                    obj.addEventListener(touchMap[type], _f, false);
+                }
+
+                return function () {
+                    obj.removeEventListener(type, f, false);
+
+                    if (supportsTouch && touchMap[type])
+                        obj.removeEventListener(touchMap[type], _f, false);
+
+                    return true;
+                };
+            };
+        } else if (g.doc.attachEvent) {
+            return function (obj, type, fn, element) {
+                var f = function (e) {
+                    e = e || g.win.event;
+                    var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
+                        scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
+                        x = e.clientX + scrollX,
+                        y = e.clientY + scrollY;
+                    e.preventDefault = e.preventDefault || preventDefault;
+                    e.stopPropagation = e.stopPropagation || stopPropagation;
+                    return fn.call(element, e, x, y);
+                };
+                obj.attachEvent("on" + type, f);
+                var detacher = function () {
+                    obj.detachEvent("on" + type, f);
+                    return true;
+                };
+                return detacher;
+            };
+        }
+    })(),
+    drag = [],
+    dragMove = function (e) {
+        var x = e.clientX,
+            y = e.clientY,
+            scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
+            scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
+            dragi,
+            j = drag.length;
+        while (j--) {
+            dragi = drag[j];
+            if (supportsTouch && e.touches) {
+                var i = e.touches.length,
+                    touch;
+                while (i--) {
+                    touch = e.touches[i];
+                    if (touch.identifier == dragi.el._drag.id) {
+                        x = touch.clientX;
+                        y = touch.clientY;
+                        (e.originalEvent ? e.originalEvent : e).preventDefault();
+                        break;
+                    }
+                }
+            } else {
+                e.preventDefault();
+            }
+            var node = dragi.el.node,
+                o,
+                next = node.nextSibling,
+                parent = node.parentNode,
+                display = node.style.display;
+            g.win.opera && parent.removeChild(node);
+            node.style.display = "none";
+            o = dragi.el.paper.getElementByPoint(x, y);
+            node.style.display = display;
+            g.win.opera && (next ? parent.insertBefore(node, next) : parent.appendChild(node));
+            o && eve("raphael.drag.over." + dragi.el.id, dragi.el, o);
+            x += scrollX;
+            y += scrollY;
+            eve("raphael.drag.move." + dragi.el.id, dragi.move_scope || dragi.el, x - dragi.el._drag.x, y - dragi.el._drag.y, x, y, e);
+        }
+    },
+    dragUp = function (e) {
+        R.unmousemove(dragMove).unmouseup(dragUp);
+        var i = drag.length,
+            dragi;
+        while (i--) {
+            dragi = drag[i];
+            dragi.el._drag = {};
+            eve("raphael.drag.end." + dragi.el.id, dragi.end_scope || dragi.start_scope || dragi.move_scope || dragi.el, e);
+        }
+        drag = [];
+    },
+    /*\
+     * Raphael.el
+     [ property (object) ]
+     **
+     * You can add your own method to elements. This is usefull when you want to hack default functionality or
+     * want to wrap some common transformation or attributes in one method. In difference to canvas methods,
+     * you can redefine element method at any time. Expending element methods wouldn’t affect set.
+     > Usage
+     | Raphael.el.red = function () {
+     |     this.attr({fill: "#f00"});
+     | };
+     | // then use it
+     | paper.circle(100, 100, 20).red();
+    \*/
+    elproto = R.el = {};
+    /*\
+     * Element.click
+     [ method ]
+     **
+     * Adds event handler for click for the element.
+     > Parameters
+     - handler (function) handler for the event
+     = (object) @Element
+    \*/
+    /*\
+     * Element.unclick
+     [ method ]
+     **
+     * Removes event handler for click for the element.
+     > Parameters
+     - handler (function) #optional handler for the event
+     = (object) @Element
+    \*/
+
+    /*\
+     * Element.dblclick
+     [ method ]
+     **
+     * Adds event handler for double click for the element.
+     > Parameters
+     - handler (function) handler for the event
+     = (object) @Element
+    \*/
+    /*\
+     * Element.undblclick
+     [ method ]
+     **
+     * Removes event handler for double click for the element.
+     > Parameters
+     - handler (function) #optional handler for the event
+     = (object) @Element
+    \*/
+
+    /*\
+     * Element.mousedown
+     [ method ]
+     **
+     * Adds event handler for mousedown for the element.
+     > Parameters
+     - handler (function) handler for the event
+     = (object) @Element
+    \*/
+    /*\
+     * Element.unmousedown
+     [ method ]
+     **
+     * Removes event handler for mousedown for the element.
+     > Parameters
+     - handler (function) #optional handler for the event
+     = (object) @Element
+    \*/
+
+    /*\
+     * Element.mousemove
+     [ method ]
+     **
+     * Adds event handler for mousemove for the element.
+     > Parameters
+     - handler (function) handler for the event
+     = (object) @Element
+    \*/
+    /*\
+     * Element.unmousemove
+     [ method ]
+     **
+     * Removes event handler for mousemove for the element.
+     > Parameters
+     - handler (function) #optional handler for the event
+     = (object) @Element
+    \*/
+
+    /*\
+     * Element.mouseout
+     [ method ]
+     **
+     * Adds event handler for mouseout for the element.
+     > Parameters
+     - handler (function) handler for the event
+     = (object) @Element
+    \*/
+    /*\
+     * Element.unmouseout
+     [ method ]
+     **
+     * Removes event handler for mouseout for the element.
+     > Parameters
+     - handler (function) #optional handler for the event
+     = (object) @Element
+    \*/
+
+    /*\
+     * Element.mouseover
+     [ method ]
+     **
+     * Adds event handler for mouseover for the element.
+     > Parameters
+     - handler (function) handler for the event
+     = (object) @Element
+    \*/
+    /*\
+     * Element.unmouseover
+     [ method ]
+     **
+     * Removes event handler for mouseover for the element.
+     > Parameters
+     - handler (function) #optional handler for the event
+     = (object) @Element
+    \*/
+
+    /*\
+     * Element.mouseup
+     [ method ]
+     **
+     * Adds event handler for mouseup for the element.
+     > Parameters
+     - handler (function) handler for the event
+     = (object) @Element
+    \*/
+    /*\
+     * Element.unmouseup
+     [ method ]
+     **
+     * Removes event handler for mouseup for the element.
+     > Parameters
+     - handler (function) #optional handler for the event
+     = (object) @Element
+    \*/
+
+    /*\
+     * Element.touchstart
+     [ method ]
+     **
+     * Adds event handler for touchstart for the element.
+     > Parameters
+     - handler (function) handler for the event
+     = (object) @Element
+    \*/
+    /*\
+     * Element.untouchstart
+     [ method ]
+     **
+     * Removes event handler for touchstart for the element.
+     > Parameters
+     - handler (function) #optional handler for the event
+     = (object) @Element
+    \*/
+
+    /*\
+     * Element.touchmove
+     [ method ]
+     **
+     * Adds event handler for touchmove for the element.
+     > Parameters
+     - handler (function) handler for the event
+     = (object) @Element
+    \*/
+    /*\
+     * Element.untouchmove
+     [ method ]
+     **
+     * Removes event handler for touchmove for the element.
+     > Parameters
+     - handler (function) #optional handler for the event
+     = (object) @Element
+    \*/
+
+    /*\
+     * Element.touchend
+     [ method ]
+     **
+     * Adds event handler for touchend for the element.
+     > Parameters
+     - handler (function) handler for the event
+     = (object) @Element
+    \*/
+    /*\
+     * Element.untouchend
+     [ method ]
+     **
+     * Removes event handler for touchend for the element.
+     > Parameters
+     - handler (function) #optional handler for the event
+     = (object) @Element
+    \*/
+
+    /*\
+     * Element.touchcancel
+     [ method ]
+     **
+     * Adds event handler for touchcancel for the element.
+     > Parameters
+     - handler (function) handler for the event
+     = (object) @Element
+    \*/
+    /*\
+     * Element.untouchcancel
+     [ method ]
+     **
+     * Removes event handler for touchcancel for the element.
+     > Parameters
+     - handler (function) #optional handler for the event
+     = (object) @Element
+    \*/
+    for (var i = events.length; i--;) {
+        (function (eventName) {
+            R[eventName] = elproto[eventName] = function (fn, scope) {
+                if (R.is(fn, "function")) {
+                    this.events = this.events || [];
+                    this.events.push({name: eventName, f: fn, unbind: addEvent(this.shape || this.node || g.doc, eventName, fn, scope || this)});
+                }
+                return this;
+            };
+            R["un" + eventName] = elproto["un" + eventName] = function (fn) {
+                var events = this.events || [],
+                    l = events.length;
+                while (l--){
+                    if (events[l].name == eventName && (R.is(fn, "undefined") || events[l].f == fn)) {
+                        events[l].unbind();
+                        events.splice(l, 1);
+                        !events.length && delete this.events;
+                    }
+                }
+                return this;
+            };
+        })(events[i]);
+    }
+
+    /*\
+     * Element.data
+     [ method ]
+     **
+     * Adds or retrieves given value asociated with given key.
+     **
+     * See also @Element.removeData
+     > Parameters
+     - key (string) key to store data
+     - value (any) #optional value to store
+     = (object) @Element
+     * or, if value is not specified:
+     = (any) value
+     * or, if key and value are not specified:
+     = (object) Key/value pairs for all the data associated with the element.
+     > Usage
+     | for (var i = 0, i < 5, i++) {
+     |     paper.circle(10 + 15 * i, 10, 10)
+     |          .attr({fill: "#000"})
+     |          .data("i", i)
+     |          .click(function () {
+     |             alert(this.data("i"));
+     |          });
+     | }
+    \*/
+    elproto.data = function (key, value) {
+        var data = eldata[this.id] = eldata[this.id] || {};
+        if (arguments.length == 0) {
+            return data;
+        }
+        if (arguments.length == 1) {
+            if (R.is(key, "object")) {
+                for (var i in key) if (key[has](i)) {
+                    this.data(i, key[i]);
+                }
+                return this;
+            }
+            eve("raphael.data.get." + this.id, this, data[key], key);
+            return data[key];
+        }
+        data[key] = value;
+        eve("raphael.data.set." + this.id, this, value, key);
+        return this;
+    };
+    /*\
+     * Element.removeData
+     [ method ]
+     **
+     * Removes value associated with an element by given key.
+     * If key is not provided, removes all the data of the element.
+     > Parameters
+     - key (string) #optional key
+     = (object) @Element
+    \*/
+    elproto.removeData = function (key) {
+        if (key == null) {
+            eldata[this.id] = {};
+        } else {
+            eldata[this.id] && delete eldata[this.id][key];
+        }
+        return this;
+    };
+     /*\
+     * Element.getData
+     [ method ]
+     **
+     * Retrieves the element data
+     = (object) data
+    \*/
+    elproto.getData = function () {
+        return clone(eldata[this.id] || {});
+    };
+    /*\
+     * Element.hover
+     [ method ]
+     **
+     * Adds event handlers for hover for the element.
+     > Parameters
+     - f_in (function) handler for hover in
+     - f_out (function) handler for hover out
+     - icontext (object) #optional context for hover in handler
+     - ocontext (object) #optional context for hover out handler
+     = (object) @Element
+    \*/
+    elproto.hover = function (f_in, f_out, scope_in, scope_out) {
+        return this.mouseover(f_in, scope_in).mouseout(f_out, scope_out || scope_in);
+    };
+    /*\
+     * Element.unhover
+     [ method ]
+     **
+     * Removes event handlers for hover for the element.
+     > Parameters
+     - f_in (function) handler for hover in
+     - f_out (function) handler for hover out
+     = (object) @Element
+    \*/
+    elproto.unhover = function (f_in, f_out) {
+        return this.unmouseover(f_in).unmouseout(f_out);
+    };
+    var draggable = [];
+    /*\
+     * Element.drag
+     [ method ]
+     **
+     * Adds event handlers for drag of the element.
+     > Parameters
+     - onmove (function) handler for moving
+     - onstart (function) handler for drag start
+     - onend (function) handler for drag end
+     - mcontext (object) #optional context for moving handler
+     - scontext (object) #optional context for drag start handler
+     - econtext (object) #optional context for drag end handler
+     * Additionaly following `drag` events will be triggered: `drag.start.<id>` on start,
+     * `drag.end.<id>` on end and `drag.move.<id>` on every move. When element will be dragged over another element
+     * `drag.over.<id>` will be fired as well.
+     *
+     * Start event and start handler will be called in specified context or in context of the element with following parameters:
+     o x (number) x position of the mouse
+     o y (number) y position of the mouse
+     o event (object) DOM event object
+     * Move event and move handler will be called in specified context or in context of the element with following parameters:
+     o dx (number) shift by x from the start point
+     o dy (number) shift by y from the start point
+     o x (number) x position of the mouse
+     o y (number) y position of the mouse
+     o event (object) DOM event object
+     * End event and end handler will be called in specified context or in context of the element with following parameters:
+     o event (object) DOM event object
+     = (object) @Element
+    \*/
+    elproto.drag = function (onmove, onstart, onend, move_scope, start_scope, end_scope) {
+        function start(e) {
+            (e.originalEvent || e).preventDefault();
+            var x = e.clientX,
+                y = e.clientY,
+                scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
+                scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft;
+            this._drag.id = e.identifier;
+            if (supportsTouch && e.touches) {
+                var i = e.touches.length, touch;
+                while (i--) {
+                    touch = e.touches[i];
+                    this._drag.id = touch.identifier;
+                    if (touch.identifier == this._drag.id) {
+                        x = touch.clientX;
+                        y = touch.clientY;
+                        break;
+                    }
+                }
+            }
+            this._drag.x = x + scrollX;
+            this._drag.y = y + scrollY;
+            !drag.length && R.mousemove(dragMove).mouseup(dragUp);
+            drag.push({el: this, move_scope: move_scope, start_scope: start_scope, end_scope: end_scope});
+            onstart && eve.on("raphael.drag.start." + this.id, onstart);
+            onmove && eve.on("raphael.drag.move." + this.id, onmove);
+            onend && eve.on("raphael.drag.end." + this.id, onend);
+            eve("raphael.drag.start." + this.id, start_scope || move_scope || this, e.clientX + scrollX, e.clientY + scrollY, e);
+        }
+        this._drag = {};
+        draggable.push({el: this, start: start});
+        this.mousedown(start);
+        return this;
+    };
+    /*\
+     * Element.onDragOver
+     [ method ]
+     **
+     * Shortcut for assigning event handler for `drag.over.<id>` event, where id is id of the element (see @Element.id).
+     > Parameters
+     - f (function) handler for event, first argument would be the element you are dragging over
+    \*/
+    elproto.onDragOver = function (f) {
+        f ? eve.on("raphael.drag.over." + this.id, f) : eve.unbind("raphael.drag.over." + this.id);
+    };
+    /*\
+     * Element.undrag
+     [ method ]
+     **
+     * Removes all drag event handlers from given element.
+    \*/
+    elproto.undrag = function () {
+        var i = draggable.length;
+        while (i--) if (draggable[i].el == this) {
+            this.unmousedown(draggable[i].start);
+            draggable.splice(i, 1);
+            eve.unbind("raphael.drag.*." + this.id);
+        }
+        !draggable.length && R.unmousemove(dragMove).unmouseup(dragUp);
+        drag = [];
+    };
+    /*\
+     * Paper.circle
+     [ method ]
+     **
+     * Draws a circle.
+     **
+     > Parameters
+     **
+     - x (number) x coordinate of the centre
+     - y (number) y coordinate of the centre
+     - r (number) radius
+     = (object) Raphaël element object with type “circle”
+     **
+     > Usage
+     | var c = paper.circle(50, 50, 40);
+    \*/
+    paperproto.circle = function (x, y, r) {
+        var out = R._engine.circle(this, x || 0, y || 0, r || 0);
+        this.__set__ && this.__set__.push(out);
+        return out;
+    };
+    /*\
+     * Paper.rect
+     [ method ]
+     *
+     * Draws a rectangle.
+     **
+     > Parameters
+     **
+     - x (number) x coordinate of the top left corner
+     - y (number) y coordinate of the top left corner
+     - width (number) width
+     - height (number) height
+     - r (number) #optional radius for rounded corners, default is 0
+     = (object) Raphaël element object with type “rect”
+     **
+     > Usage
+     | // regular rectangle
+     | var c = paper.rect(10, 10, 50, 50);
+     | // rectangle with rounded corners
+     | var c = paper.rect(40, 40, 50, 50, 10);
+    \*/
+    paperproto.rect = function (x, y, w, h, r) {
+        var out = R._engine.rect(this, x || 0, y || 0, w || 0, h || 0, r || 0);
+        this.__set__ && this.__set__.push(out);
+        return out;
+    };
+    /*\
+     * Paper.ellipse
+     [ method ]
+     **
+     * Draws an ellipse.
+     **
+     > Parameters
+     **
+     - x (number) x coordinate of the centre
+     - y (number) y coordinate of the centre
+     - rx (number) horizontal radius
+     - ry (number) vertical radius
+     = (object) Raphaël element object with type “ellipse”
+     **
+     > Usage
+     | var c = paper.ellipse(50, 50, 40, 20);
+    \*/
+    paperproto.ellipse = function (x, y, rx, ry) {
+        var out = R._engine.ellipse(this, x || 0, y || 0, rx || 0, ry || 0);
+        this.__set__ && this.__set__.push(out);
+        return out;
+    };
+    /*\
+     * Paper.path
+     [ method ]
+     **
+     * Creates a path element by given path data string.
+     > Parameters
+     - pathString (string) #optional path string in SVG format.
+     * Path string consists of one-letter commands, followed by comma seprarated arguments in numercal form. Example:
+     | "M10,20L30,40"
+     * Here we can see two commands: “M”, with arguments `(10, 20)` and “L” with arguments `(30, 40)`. Upper case letter mean command is absolute, lower case—relative.
+     *
+     # <p>Here is short list of commands available, for more details see <a href="http://www.w3.org/TR/SVG/paths.html#PathData" title="Details of a path's data attribute's format are described in the SVG specification.">SVG path string format</a>.</p>
+     # <table><thead><tr><th>Command</th><th>Name</th><th>Parameters</th></tr></thead><tbody>
+     # <tr><td>M</td><td>moveto</td><td>(x y)+</td></tr>
+     # <tr><td>Z</td><td>closepath</td><td>(none)</td></tr>
+     # <tr><td>L</td><td>lineto</td><td>(x y)+</td></tr>
+     # <tr><td>H</td><td>horizontal lineto</td><td>x+</td></tr>
+     # <tr><td>V</td><td>vertical lineto</td><td>y+</td></tr>
+     # <tr><td>C</td><td>curveto</td><td>(x1 y1 x2 y2 x y)+</td></tr>
+     # <tr><td>S</td><td>smooth curveto</td><td>(x2 y2 x y)+</td></tr>
+     # <tr><td>Q</td><td>quadratic Bézier curveto</td><td>(x1 y1 x y)+</td></tr>
+     # <tr><td>T</td><td>smooth quadratic Bézier curveto</td><td>(x y)+</td></tr>
+     # <tr><td>A</td><td>elliptical arc</td><td>(rx ry x-axis-rotation large-arc-flag sweep-flag x y)+</td></tr>
+     # <tr><td>R</td><td><a href="http://en.wikipedia.org/wiki/Catmull–Rom_spline#Catmull.E2.80.93Rom_spline">Catmull-Rom curveto</a>*</td><td>x1 y1 (x y)+</td></tr></tbody></table>
+     * * “Catmull-Rom curveto” is a not standard SVG command and added in 2.0 to make life easier.
+     * Note: there is a special case when path consist of just three commands: “M10,10R…z”. In this case path will smoothly connects to its beginning.
+     > Usage
+     | var c = paper.path("M10 10L90 90");
+     | // draw a diagonal line:
+     | // move to 10,10, line to 90,90
+     * For example of path strings, check out these icons: http://raphaeljs.com/icons/
+    \*/
+    paperproto.path = function (pathString) {
+        pathString && !R.is(pathString, string) && !R.is(pathString[0], array) && (pathString += E);
+        var out = R._engine.path(R.format[apply](R, arguments), this);
+        this.__set__ && this.__set__.push(out);
+        return out;
+    };
+    /*\
+     * Paper.image
+     [ method ]
+     **
+     * Embeds an image into the surface.
+     **
+     > Parameters
+     **
+     - src (string) URI of the source image
+     - x (number) x coordinate position
+     - y (number) y coordinate position
+     - width (number) width of the image
+     - height (number) height of the image
+     = (object) Raphaël element object with type “image”
+     **
+     > Usage
+     | var c = paper.image("apple.png", 10, 10, 80, 80);
+    \*/
+    paperproto.image = function (src, x, y, w, h) {
+        var out = R._engine.image(this, src || "about:blank", x || 0, y || 0, w || 0, h || 0);
+        this.__set__ && this.__set__.push(out);
+        return out;
+    };
+    /*\
+     * Paper.text
+     [ method ]
+     **
+     * Draws a text string. If you need line breaks, put “\n” in the string.
+     **
+     > Parameters
+     **
+     - x (number) x coordinate position
+     - y (number) y coordinate position
+     - text (string) The text string to draw
+     = (object) Raphaël element object with type “text”
+     **
+     > Usage
+     | var t = paper.text(50, 50, "Raphaël\nkicks\nbutt!");
+    \*/
+    paperproto.text = function (x, y, text) {
+        var out = R._engine.text(this, x || 0, y || 0, Str(text));
+        this.__set__ && this.__set__.push(out);
+        return out;
+    };
+    /*\
+     * Paper.set
+     [ method ]
+     **
+     * Creates array-like object to keep and operate several elements at once.
+     * Warning: it doesn’t create any elements for itself in the page, it just groups existing elements.
+     * Sets act as pseudo elements — all methods available to an element can be used on a set.
+     = (object) array-like object that represents set of elements
+     **
+     > Usage
+     | var st = paper.set();
+     | st.push(
+     |     paper.circle(10, 10, 5),
+     |     paper.circle(30, 10, 5)
+     | );
+     | st.attr({fill: "red"}); // changes the fill of both circles
+    \*/
+    paperproto.set = function (itemsArray) {
+        !R.is(itemsArray, "array") && (itemsArray = Array.prototype.splice.call(arguments, 0, arguments.length));
+        var out = new Set(itemsArray);
+        this.__set__ && this.__set__.push(out);
+        out["paper"] = this;
+        out["type"] = "set";
+        return out;
+    };
+    /*\
+     * Paper.setStart
+     [ method ]
+     **
+     * Creates @Paper.set. All elements that will be created after calling this method and before calling
+     * @Paper.setFinish will be added to the set.
+     **
+     > Usage
+     | paper.setStart();
+     | paper.circle(10, 10, 5),
+     | paper.circle(30, 10, 5)
+     | var st = paper.setFinish();
+     | st.attr({fill: "red"}); // changes the fill of both circles
+    \*/
+    paperproto.setStart = function (set) {
+        this.__set__ = set || this.set();
+    };
+    /*\
+     * Paper.setFinish
+     [ method ]
+     **
+     * See @Paper.setStart. This method finishes catching and returns resulting set.
+     **
+     = (object) set
+    \*/
+    paperproto.setFinish = function (set) {
+        var out = this.__set__;
+        delete this.__set__;
+        return out;
+    };
+    /*\
+     * Paper.getSize
+     [ method ]
+     **
+     * Obtains current paper actual size.
+     **
+     = (object)
+     \*/
+    paperproto.getSize = function () {
+        var container = this.canvas.parentNode;
+        return {
+            width: container.offsetWidth,
+            height: container.offsetHeight
+                };
+        };
+    /*\
+     * Paper.setSize
+     [ method ]
+     **
+     * If you need to change dimensions of the canvas call this method
+     **
+     > Parameters
+     **
+     - width (number) new width of the canvas
+     - height (number) new height of the canvas
+    \*/
+    paperproto.setSize = function (width, height) {
+        return R._engine.setSize.call(this, width, height);
+    };
+    /*\
+     * Paper.setViewBox
+     [ method ]
+     **
+     * Sets the view box of the paper. Practically it gives you ability to zoom and pan whole paper surface by
+     * specifying new boundaries.
+     **
+     > Parameters
+     **
+     - x (number) new x position, default is `0`
+     - y (number) new y position, default is `0`
+     - w (number) new width of the canvas
+     - h (number) new height of the canvas
+     - fit (boolean) `true` if you want graphics to fit into new boundary box
+    \*/
+    paperproto.setViewBox = function (x, y, w, h, fit) {
+        return R._engine.setViewBox.call(this, x, y, w, h, fit);
+    };
+    /*\
+     * Paper.top
+     [ property ]
+     **
+     * Points to the topmost element on the paper
+    \*/
+    /*\
+     * Paper.bottom
+     [ property ]
+     **
+     * Points to the bottom element on the paper
+    \*/
+    paperproto.top = paperproto.bottom = null;
+    /*\
+     * Paper.raphael
+     [ property ]
+     **
+     * Points to the @Raphael object/function
+    \*/
+    paperproto.raphael = R;
+    var getOffset = function (elem) {
+        var box = elem.getBoundingClientRect(),
+            doc = elem.ownerDocument,
+            body = doc.body,
+            docElem = doc.documentElement,
+            clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
+            top  = box.top  + (g.win.pageYOffset || docElem.scrollTop || body.scrollTop ) - clientTop,
+            left = box.left + (g.win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft;
+        return {
+            y: top,
+            x: left
+        };
+    };
+    /*\
+     * Paper.getElementByPoint
+     [ method ]
+     **
+     * Returns you topmost element under given point.
+     **
+     = (object) Raphaël element object
+     > Parameters
+     **
+     - x (number) x coordinate from the top left corner of the window
+     - y (number) y coordinate from the top left corner of the window
+     > Usage
+     | paper.getElementByPoint(mouseX, mouseY).attr({stroke: "#f00"});
+    \*/
+    paperproto.getElementByPoint = function (x, y) {
+        var paper = this,
+            svg = paper.canvas,
+            target = g.doc.elementFromPoint(x, y);
+        if (g.win.opera && target.tagName == "svg") {
+            var so = getOffset(svg),
+                sr = svg.createSVGRect();
+            sr.x = x - so.x;
+            sr.y = y - so.y;
+            sr.width = sr.height = 1;
+            var hits = svg.getIntersectionList(sr, null);
+            if (hits.length) {
+                target = hits[hits.length - 1];
+            }
+        }
+        if (!target) {
+            return null;
+        }
+        while (target.parentNode && target != svg.parentNode && !target.raphael) {
+            target = target.parentNode;
+        }
+        target == paper.canvas.parentNode && (target = svg);
+        target = target && target.raphael ? paper.getById(target.raphaelid) : null;
+        return target;
+    };
+
+    /*\
+     * Paper.getElementsByBBox
+     [ method ]
+     **
+     * Returns set of elements that have an intersecting bounding box
+     **
+     > Parameters
+     **
+     - bbox (object) bbox to check with
+     = (object) @Set
+     \*/
+    paperproto.getElementsByBBox = function (bbox) {
+        var set = this.set();
+        this.forEach(function (el) {
+            if (R.isBBoxIntersect(el.getBBox(), bbox)) {
+                set.push(el);
+            }
+        });
+        return set;
+    };
+
+    /*\
+     * Paper.getById
+     [ method ]
+     **
+     * Returns you element by its internal ID.
+     **
+     > Parameters
+     **
+     - id (number) id
+     = (object) Raphaël element object
+    \*/
+    paperproto.getById = function (id) {
+        var bot = this.bottom;
+        while (bot) {
+            if (bot.id == id) {
+                return bot;
+            }
+            bot = bot.next;
+        }
+        return null;
+    };
+    /*\
+     * Paper.forEach
+     [ method ]
+     **
+     * Executes given function for each element on the paper
+     *
+     * If callback function returns `false` it will stop loop running.
+     **
+     > Parameters
+     **
+     - callback (function) function to run
+     - thisArg (object) context object for the callback
+     = (object) Paper object
+     > Usage
+     | paper.forEach(function (el) {
+     |     el.attr({ stroke: "blue" });
+     | });
+    \*/
+    paperproto.forEach = function (callback, thisArg) {
+        var bot = this.bottom;
+        while (bot) {
+            if (callback.call(thisArg, bot) === false) {
+                return this;
+            }
+            bot = bot.next;
+        }
+        return this;
+    };
+    /*\
+     * Paper.getElementsByPoint
+     [ method ]
+     **
+     * Returns set of elements that have common point inside
+     **
+     > Parameters
+     **
+     - x (number) x coordinate of the point
+     - y (number) y coordinate of the point
+     = (object) @Set
+    \*/
+    paperproto.getElementsByPoint = function (x, y) {
+        var set = this.set();
+        this.forEach(function (el) {
+            if (el.isPointInside(x, y)) {
+                set.push(el);
+            }
+        });
+        return set;
+    };
+    function x_y() {
+        return this.x + S + this.y;
+    }
+    function x_y_w_h() {
+        return this.x + S + this.y + S + this.width + " \xd7 " + this.height;
+    }
+    /*\
+     * Element.isPointInside
+     [ method ]
+     **
+     * Determine if given point is inside this element’s shape
+     **
+     > Parameters
+     **
+     - x (number) x coordinate of the point
+     - y (number) y coordinate of the point
+     = (boolean) `true` if point inside the shape
+    \*/
+    elproto.isPointInside = function (x, y) {
+        var rp = this.realPath = getPath[this.type](this);
+        if (this.attr('transform') && this.attr('transform').length) {
+            rp = R.transformPath(rp, this.attr('transform'));
+        }
+        return R.isPointInsidePath(rp, x, y);
+    };
+    /*\
+     * Element.getBBox
+     [ method ]
+     **
+     * Return bounding box for a given element
+     **
+     > Parameters
+     **
+     - isWithoutTransform (boolean) flag, `true` if you want to have bounding box before transformations. Default is `false`.
+     = (object) Bounding box object:
+     o {
+     o     x: (number) top left corner x
+     o     y: (number) top left corner y
+     o     x2: (number) bottom right corner x
+     o     y2: (number) bottom right corner y
+     o     width: (number) width
+     o     height: (number) height
+     o }
+    \*/
+    elproto.getBBox = function (isWithoutTransform) {
+        if (this.removed) {
+            return {};
+        }
+        var _ = this._;
+        if (isWithoutTransform) {
+            if (_.dirty || !_.bboxwt) {
+                this.realPath = getPath[this.type](this);
+                _.bboxwt = pathDimensions(this.realPath);
+                _.bboxwt.toString = x_y_w_h;
+                _.dirty = 0;
+            }
+            return _.bboxwt;
+        }
+        if (_.dirty || _.dirtyT || !_.bbox) {
+            if (_.dirty || !this.realPath) {
+                _.bboxwt = 0;
+                this.realPath = getPath[this.type](this);
+            }
+            _.bbox = pathDimensions(mapPath(this.realPath, this.matrix));
+            _.bbox.toString = x_y_w_h;
+            _.dirty = _.dirtyT = 0;
+        }
+        return _.bbox;
+    };
+    /*\
+     * Element.clone
+     [ method ]
+     **
+     = (object) clone of a given element
+     **
+    \*/
+    elproto.clone = function () {
+        if (this.removed) {
+            return null;
+        }
+        var out = this.paper[this.type]().attr(this.attr());
+        this.__set__ && this.__set__.push(out);
+        return out;
+    };
+    /*\
+     * Element.glow
+     [ method ]
+     **
+     * Return set of elements that create glow-like effect around given element. See @Paper.set.
+     *
+     * Note: Glow is not connected to the element. If you change element attributes it won’t adjust itself.
+     **
+     > Parameters
+     **
+     - glow (object) #optional parameters object with all properties optional:
+     o {
+     o     width (number) size of the glow, default is `10`
+     o     fill (boolean) will it be filled, default is `false`
+     o     opacity (number) opacity, default is `0.5`
+     o     offsetx (number) horizontal offset, default is `0`
+     o     offsety (number) vertical offset, default is `0`
+     o     color (string) glow colour, default is `black`
+     o }
+     = (object) @Paper.set of elements that represents glow
+    \*/
+    elproto.glow = function (glow) {
+        if (this.type == "text") {
+            return null;
+        }
+        glow = glow || {};
+        var s = {
+            width: (glow.width || 10) + (+this.attr("stroke-width") || 1),
+            fill: glow.fill || false,
+            opacity: glow.opacity || .5,
+            offsetx: glow.offsetx || 0,
+            offsety: glow.offsety || 0,
+            color: glow.color || "#000"
+        },
+            c = s.width / 2,
+            r = this.paper,
+            out = r.set(),
+            path = this.realPath || getPath[this.type](this);
+        path = this.matrix ? mapPath(path, this.matrix) : path;
+        for (var i = 1; i < c + 1; i++) {
+            out.push(r.path(path).attr({
+                stroke: s.color,
+                fill: s.fill ? s.color : "none",
+                "stroke-linejoin": "round",
+                "stroke-linecap": "round",
+                "stroke-width": +(s.width / c * i).toFixed(3),
+                opacity: +(s.opacity / c).toFixed(3)
+            }));
+        }
+        return out.insertBefore(this).translate(s.offsetx, s.offsety);
+    };
+    var curveslengths = {},
+    getPointAtSegmentLength = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) {
+        if (length == null) {
+            return bezlen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y);
+        } else {
+            return R.findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, getTatLen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length));
+        }
+    },
+    getLengthFactory = function (istotal, subpath) {
+        return function (path, length, onlystart) {
+            path = path2curve(path);
+            var x, y, p, l, sp = "", subpaths = {}, point,
+                len = 0;
+            for (var i = 0, ii = path.length; i < ii; i++) {
+                p = path[i];
+                if (p[0] == "M") {
+                    x = +p[1];
+                    y = +p[2];
+                } else {
+                    l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
+                    if (len + l > length) {
+                        if (subpath && !subpaths.start) {
+                            point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
+                            sp += ["C" + point.start.x, point.start.y, point.m.x, point.m.y, point.x, point.y];
+                            if (onlystart) {return sp;}
+                            subpaths.start = sp;
+                            sp = ["M" + point.x, point.y + "C" + point.n.x, point.n.y, point.end.x, point.end.y, p[5], p[6]].join();
+                            len += l;
+                            x = +p[5];
+                            y = +p[6];
+                            continue;
+                        }
+                        if (!istotal && !subpath) {
+                            point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
+                            return {x: point.x, y: point.y, alpha: point.alpha};
+                        }
+                    }
+                    len += l;
+                    x = +p[5];
+                    y = +p[6];
+                }
+                sp += p.shift() + p;
+            }
+            subpaths.end = sp;
+            point = istotal ? len : subpath ? subpaths : R.findDotsAtSegment(x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1);
+            point.alpha && (point = {x: point.x, y: point.y, alpha: point.alpha});
+            return point;
+        };
+    };
+    var getTotalLength = getLengthFactory(1),
+        getPointAtLength = getLengthFactory(),
+        getSubpathsAtLength = getLengthFactory(0, 1);
+    /*\
+     * Raphael.getTotalLength
+     [ method ]
+     **
+     * Returns length of the given path in pixels.
+     **
+     > Parameters
+     **
+     - path (string) SVG path string.
+     **
+     = (number) length.
+    \*/
+    R.getTotalLength = getTotalLength;
+    /*\
+     * Raphael.getPointAtLength
+     [ method ]
+     **
+     * Return coordinates of the point located at the given length on the given path.
+     **
+     > Parameters
+     **
+     - path (string) SVG path string
+     - length (number)
+     **
+     = (object) representation of the point:
+     o {
+     o     x: (number) x coordinate
+     o     y: (number) y coordinate
+     o     alpha: (number) angle of derivative
+     o }
+    \*/
+    R.getPointAtLength = getPointAtLength;
+    /*\
+     * Raphael.getSubpath
+     [ method ]
+     **
+     * Return subpath of a given path from given length to given length.
+     **
+     > Parameters
+     **
+     - path (string) SVG path string
+     - from (number) position of the start of the segment
+     - to (number) position of the end of the segment
+     **
+     = (string) pathstring for the segment
+    \*/
+    R.getSubpath = function (path, from, to) {
+        if (this.getTotalLength(path) - to < 1e-6) {
+            return getSubpathsAtLength(path, from).end;
+        }
+        var a = getSubpathsAtLength(path, to, 1);
+        return from ? getSubpathsAtLength(a, from).end : a;
+    };
+    /*\
+     * Element.getTotalLength
+     [ method ]
+     **
+     * Returns length of the path in pixels. Only works for element of “path” type.
+     = (number) length.
+    \*/
+    elproto.getTotalLength = function () {
+        var path = this.getPath();
+        if (!path) {
+            return;
+        }
+
+        if (this.node.getTotalLength) {
+            return this.node.getTotalLength();
+        }
+
+        return getTotalLength(path);
+    };
+    /*\
+     * Element.getPointAtLength
+     [ method ]
+     **
+     * Return coordinates of the point located at the given length on the given path. Only works for element of “path” type.
+     **
+     > Parameters
+     **
+     - length (number)
+     **
+     = (object) representation of the point:
+     o {
+     o     x: (number) x coordinate
+     o     y: (number) y coordinate
+     o     alpha: (number) angle of derivative
+     o }
+    \*/
+    elproto.getPointAtLength = function (length) {
+        var path = this.getPath();
+        if (!path) {
+            return;
+        }
+
+        return getPointAtLength(path, length);
+    };
+    /*\
+     * Element.getPath
+     [ method ]
+     **
+     * Returns path of the element. Only works for elements of “path” type and simple elements like circle.
+     = (object) path
+     **
+    \*/
+    elproto.getPath = function () {
+        var path,
+            getPath = R._getPath[this.type];
+
+        if (this.type == "text" || this.type == "set") {
+            return;
+        }
+
+        if (getPath) {
+            path = getPath(this);
+        }
+
+        return path;
+    };
+    /*\
+     * Element.getSubpath
+     [ method ]
+     **
+     * Return subpath of a given element from given length to given length. Only works for element of “path” type.
+     **
+     > Parameters
+     **
+     - from (number) position of the start of the segment
+     - to (number) position of the end of the segment
+     **
+     = (string) pathstring for the segment
+    \*/
+    elproto.getSubpath = function (from, to) {
+        var path = this.getPath();
+        if (!path) {
+            return;
+        }
+
+        return R.getSubpath(path, from, to);
+    };
+    /*\
+     * Raphael.easing_formulas
+     [ property ]
+     **
+     * Object that contains easing formulas for animation. You could extend it with your own. By default it has following list of easing:
+     # <ul>
+     #     <li>“linear”</li>
+     #     <li>“&lt;” or “easeIn” or “ease-in”</li>
+     #     <li>“>” or “easeOut” or “ease-out”</li>
+     #     <li>“&lt;>” or “easeInOut” or “ease-in-out”</li>
+     #     <li>“backIn” or “back-in”</li>
+     #     <li>“backOut” or “back-out”</li>
+     #     <li>“elastic”</li>
+     #     <li>“bounce”</li>
+     # </ul>
+     # <p>See also <a href="http://raphaeljs.com/easing.html">Easing demo</a>.</p>
+    \*/
+    var ef = R.easing_formulas = {
+        linear: function (n) {
+            return n;
+        },
+        "<": function (n) {
+            return pow(n, 1.7);
+        },
+        ">": function (n) {
+            return pow(n, .48);
+        },
+        "<>": function (n) {
+            var q = .48 - n / 1.04,
+                Q = math.sqrt(.1734 + q * q),
+                x = Q - q,
+                X = pow(abs(x), 1 / 3) * (x < 0 ? -1 : 1),
+                y = -Q - q,
+                Y = pow(abs(y), 1 / 3) * (y < 0 ? -1 : 1),
+                t = X + Y + .5;
+            return (1 - t) * 3 * t * t + t * t * t;
+        },
+        backIn: function (n) {
+            var s = 1.70158;
+            return n * n * ((s + 1) * n - s);
+        },
+        backOut: function (n) {
+            n = n - 1;
+            var s = 1.70158;
+            return n * n * ((s + 1) * n + s) + 1;
+        },
+        elastic: function (n) {
+            if (n == !!n) {
+                return n;
+            }
+            return pow(2, -10 * n) * math.sin((n - .075) * (2 * PI) / .3) + 1;
+        },
+        bounce: function (n) {
+            var s = 7.5625,
+                p = 2.75,
+                l;
+            if (n < (1 / p)) {
+                l = s * n * n;
+            } else {
+                if (n < (2 / p)) {
+                    n -= (1.5 / p);
+                    l = s * n * n + .75;
+                } else {
+                    if (n < (2.5 / p)) {
+                        n -= (2.25 / p);
+                        l = s * n * n + .9375;
+                    } else {
+                        n -= (2.625 / p);
+                        l = s * n * n + .984375;
+                    }
+                }
+            }
+            return l;
+        }
+    };
+    ef.easeIn = ef["ease-in"] = ef["<"];
+    ef.easeOut = ef["ease-out"] = ef[">"];
+    ef.easeInOut = ef["ease-in-out"] = ef["<>"];
+    ef["back-in"] = ef.backIn;
+    ef["back-out"] = ef.backOut;
+
+    var animationElements = [],
+        requestAnimFrame = window.requestAnimationFrame       ||
+                           window.webkitRequestAnimationFrame ||
+                           window.mozRequestAnimationFrame    ||
+                           window.oRequestAnimationFrame      ||
+                           window.msRequestAnimationFrame     ||
+                           function (callback) {
+                               setTimeout(callback, 16);
+                           },
+        animation = function () {
+            var Now = +new Date,
+                l = 0;
+            for (; l < animationElements.length; l++) {
+                var e = animationElements[l];
+                if (e.el.removed || e.paused) {
+                    continue;
+                }
+                var time = Now - e.start,
+                    ms = e.ms,
+                    easing = e.easing,
+                    from = e.from,
+                    diff = e.diff,
+                    to = e.to,
+                    t = e.t,
+                    that = e.el,
+                    set = {},
+                    now,
+                    init = {},
+                    key;
+                if (e.initstatus) {
+                    time = (e.initstatus * e.anim.top - e.prev) / (e.percent - e.prev) * ms;
+                    e.status = e.initstatus;
+                    delete e.initstatus;
+                    e.stop && animationElements.splice(l--, 1);
+                } else {
+                    e.status = (e.prev + (e.percent - e.prev) * (time / ms)) / e.anim.top;
+                }
+                if (time < 0) {
+                    continue;
+                }
+                if (time < ms) {
+                    var pos = easing(time / ms);
+                    for (var attr in from) if (from[has](attr)) {
+                        switch (availableAnimAttrs[attr]) {
+                            case nu:
+                                now = +from[attr] + pos * ms * diff[attr];
+                                break;
+                            case "colour":
+                                now = "rgb(" + [
+                                    upto255(round(from[attr].r + pos * ms * diff[attr].r)),
+                                    upto255(round(from[attr].g + pos * ms * diff[attr].g)),
+                                    upto255(round(from[attr].b + pos * ms * diff[attr].b))
+                                ].join(",") + ")";
+                                break;
+                            case "path":
+                                now = [];
+                                for (var i = 0, ii = from[attr].length; i < ii; i++) {
+                                    now[i] = [from[attr][i][0]];
+                                    for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
+                                        now[i][j] = +from[attr][i][j] + pos * ms * diff[attr][i][j];
+                                    }
+                                    now[i] = now[i].join(S);
+                                }
+                                now = now.join(S);
+                                break;
+                            case "transform":
+                                if (diff[attr].real) {
+                                    now = [];
+                                    for (i = 0, ii = from[attr].length; i < ii; i++) {
+                                        now[i] = [from[attr][i][0]];
+                                        for (j = 1, jj = from[attr][i].length; j < jj; j++) {
+                                            now[i][j] = from[attr][i][j] + pos * ms * diff[attr][i][j];
+                                        }
+                                    }
+                                } else {
+                                    var get = function (i) {
+                                        return +from[attr][i] + pos * ms * diff[attr][i];
+                                    };
+                                    // now = [["r", get(2), 0, 0], ["t", get(3), get(4)], ["s", get(0), get(1), 0, 0]];
+                                    now = [["m", get(0), get(1), get(2), get(3), get(4), get(5)]];
+                                }
+                                break;
+                            case "csv":
+                                if (attr == "clip-rect") {
+                                    now = [];
+                                    i = 4;
+                                    while (i--) {
+                                        now[i] = +from[attr][i] + pos * ms * diff[attr][i];
+                                    }
+                                }
+                                break;
+                            default:
+                                var from2 = [][concat](from[attr]);
+                                now = [];
+                                i = that.paper.customAttributes[attr].length;
+                                while (i--) {
+                                    now[i] = +from2[i] + pos * ms * diff[attr][i];
+                                }
+                                break;
+                        }
+                        set[attr] = now;
+                    }
+                    that.attr(set);
+                    (function (id, that, anim) {
+                        setTimeout(function () {
+                            eve("raphael.anim.frame." + id, that, anim);
+                        });
+                    })(that.id, that, e.anim);
+                } else {
+                    (function(f, el, a) {
+                        setTimeout(function() {
+                            eve("raphael.anim.frame." + el.id, el, a);
+                            eve("raphael.anim.finish." + el.id, el, a);
+                            R.is(f, "function") && f.call(el);
+                        });
+                    })(e.callback, that, e.anim);
+                    that.attr(to);
+                    animationElements.splice(l--, 1);
+                    if (e.repeat > 1 && !e.next) {
+                        for (key in to) if (to[has](key)) {
+                            init[key] = e.totalOrigin[key];
+                        }
+                        e.el.attr(init);
+                        runAnimation(e.anim, e.el, e.anim.percents[0], null, e.totalOrigin, e.repeat - 1);
+                    }
+                    if (e.next && !e.stop) {
+                        runAnimation(e.anim, e.el, e.next, null, e.totalOrigin, e.repeat);
+                    }
+                }
+            }
+            R.svg && that && that.paper && that.paper.safari();
+            animationElements.length && requestAnimFrame(animation);
+        },
+        upto255 = function (color) {
+            return color > 255 ? 255 : color < 0 ? 0 : color;
+        };
+    /*\
+     * Element.animateWith
+     [ method ]
+     **
+     * Acts similar to @Element.animate, but ensure that given animation runs in sync with another given element.
+     **
+     > Parameters
+     **
+     - el (object) element to sync with
+     - anim (object) animation to sync with
+     - params (object) #optional final attributes for the element, see also @Element.attr
+     - ms (number) #optional number of milliseconds for animation to run
+     - easing (string) #optional easing type. Accept on of @Raphael.easing_formulas or CSS format: `cubic&#x2010;bezier(XX,&#160;XX,&#160;XX,&#160;XX)`
+     - callback (function) #optional callback function. Will be called at the end of animation.
+     * or
+     - element (object) element to sync with
+     - anim (object) animation to sync with
+     - animation (object) #optional animation object, see @Raphael.animation
+     **
+     = (object) original element
+    \*/
+    elproto.animateWith = function (el, anim, params, ms, easing, callback) {
+        var element = this;
+        if (element.removed) {
+            callback && callback.call(element);
+            return element;
+        }
+        var a = params instanceof Animation ? params : R.animation(params, ms, easing, callback),
+            x, y;
+        runAnimation(a, element, a.percents[0], null, element.attr());
+        for (var i = 0, ii = animationElements.length; i < ii; i++) {
+            if (animationElements[i].anim == anim && animationElements[i].el == el) {
+                animationElements[ii - 1].start = animationElements[i].start;
+                break;
+            }
+        }
+        return element;
+        //
+        //
+        // var a = params ? R.animation(params, ms, easing, callback) : anim,
+        //     status = element.status(anim);
+        // return this.animate(a).status(a, status * anim.ms / a.ms);
+    };
+    function CubicBezierAtTime(t, p1x, p1y, p2x, p2y, duration) {
+        var cx = 3 * p1x,
+            bx = 3 * (p2x - p1x) - cx,
+            ax = 1 - cx - bx,
+            cy = 3 * p1y,
+            by = 3 * (p2y - p1y) - cy,
+            ay = 1 - cy - by;
+        function sampleCurveX(t) {
+            return ((ax * t + bx) * t + cx) * t;
+        }
+        function solve(x, epsilon) {
+            var t = solveCurveX(x, epsilon);
+            return ((ay * t + by) * t + cy) * t;
+        }
+        function solveCurveX(x, epsilon) {
+            var t0, t1, t2, x2, d2, i;
+            for(t2 = x, i = 0; i < 8; i++) {
+                x2 = sampleCurveX(t2) - x;
+                if (abs(x2) < epsilon) {
+                    return t2;
+                }
+                d2 = (3 * ax * t2 + 2 * bx) * t2 + cx;
+                if (abs(d2) < 1e-6) {
+                    break;
+                }
+                t2 = t2 - x2 / d2;
+            }
+            t0 = 0;
+            t1 = 1;
+            t2 = x;
+            if (t2 < t0) {
+                return t0;
+            }
+            if (t2 > t1) {
+                return t1;
+            }
+            while (t0 < t1) {
+                x2 = sampleCurveX(t2);
+                if (abs(x2 - x) < epsilon) {
+                    return t2;
+                }
+                if (x > x2) {
+                    t0 = t2;
+                } else {
+                    t1 = t2;
+                }
+                t2 = (t1 - t0) / 2 + t0;
+            }
+            return t2;
+        }
+        return solve(t, 1 / (200 * duration));
+    }
+    elproto.onAnimation = function (f) {
+        f ? eve.on("raphael.anim.frame." + this.id, f) : eve.unbind("raphael.anim.frame." + this.id);
+        return this;
+    };
+    function Animation(anim, ms) {
+        var percents = [],
+            newAnim = {};
+        this.ms = ms;
+        this.times = 1;
+        if (anim) {
+            for (var attr in anim) if (anim[has](attr)) {
+                newAnim[toFloat(attr)] = anim[attr];
+                percents.push(toFloat(attr));
+            }
+            percents.sort(sortByNumber);
+        }
+        this.anim = newAnim;
+        this.top = percents[percents.length - 1];
+        this.percents = percents;
+    }
+    /*\
+     * Animation.delay
+     [ method ]
+     **
+     * Creates a copy of existing animation object with given delay.
+     **
+     > Parameters
+     **
+     - delay (number) number of ms to pass between animation start and actual animation
+     **
+     = (object) new altered Animation object
+     | var anim = Raphael.animation({cx: 10, cy: 20}, 2e3);
+     | circle1.animate(anim); // run the given animation immediately
+     | circle2.animate(anim.delay(500)); // run the given animation after 500 ms
+    \*/
+    Animation.prototype.delay = function (delay) {
+        var a = new Animation(this.anim, this.ms);
+        a.times = this.times;
+        a.del = +delay || 0;
+        return a;
+    };
+    /*\
+     * Animation.repeat
+     [ method ]
+     **
+     * Creates a copy of existing animation object with given repetition.
+     **
+     > Parameters
+     **
+     - repeat (number) number iterations of animation. For infinite animation pass `Infinity`
+     **
+     = (object) new altered Animation object
+    \*/
+    Animation.prototype.repeat = function (times) {
+        var a = new Animation(this.anim, this.ms);
+        a.del = this.del;
+        a.times = math.floor(mmax(times, 0)) || 1;
+        return a;
+    };
+    function runAnimation(anim, element, percent, status, totalOrigin, times) {
+        percent = toFloat(percent);
+        var params,
+            isInAnim,
+            isInAnimSet,
+            percents = [],
+            next,
+            prev,
+            timestamp,
+            ms = anim.ms,
+            from = {},
+            to = {},
+            diff = {};
+        if (status) {
+            for (i = 0, ii = animationElements.length; i < ii; i++) {
+                var e = animationElements[i];
+                if (e.el.id == element.id && e.anim == anim) {
+                    if (e.percent != percent) {
+                        animationElements.splice(i, 1);
+                        isInAnimSet = 1;
+                    } else {
+                        isInAnim = e;
+                    }
+                    element.attr(e.totalOrigin);
+                    break;
+                }
+            }
+        } else {
+            status = +to; // NaN
+        }
+        for (var i = 0, ii = anim.percents.length; i < ii; i++) {
+            if (anim.percents[i] == percent || anim.percents[i] > status * anim.top) {
+                percent = anim.percents[i];
+                prev = anim.percents[i - 1] || 0;
+                ms = ms / anim.top * (percent - prev);
+                next = anim.percents[i + 1];
+                params = anim.anim[percent];
+                break;
+            } else if (status) {
+                element.attr(anim.anim[anim.percents[i]]);
+            }
+        }
+        if (!params) {
+            return;
+        }
+        if (!isInAnim) {
+            for (var attr in params) if (params[has](attr)) {
+                if (availableAnimAttrs[has](attr) || element.paper.customAttributes[has](attr)) {
+                    from[attr] = element.attr(attr);
+                    (from[attr] == null) && (from[attr] = availableAttrs[attr]);
+                    to[attr] = params[attr];
+                    switch (availableAnimAttrs[attr]) {
+                        case nu:
+                            diff[attr] = (to[attr] - from[attr]) / ms;
+                            break;
+                        case "colour":
+                            from[attr] = R.getRGB(from[attr]);
+                            var toColour = R.getRGB(to[attr]);
+                            diff[attr] = {
+                                r: (toColour.r - from[attr].r) / ms,
+                                g: (toColour.g - from[attr].g) / ms,
+                                b: (toColour.b - from[attr].b) / ms
+                            };
+                            break;
+                        case "path":
+                            var pathes = path2curve(from[attr], to[attr]),
+                                toPath = pathes[1];
+                            from[attr] = pathes[0];
+                            diff[attr] = [];
+                            for (i = 0, ii = from[attr].length; i < ii; i++) {
+                                diff[attr][i] = [0];
+                                for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
+                                    diff[attr][i][j] = (toPath[i][j] - from[attr][i][j]) / ms;
+                                }
+                            }
+                            break;
+                        case "transform":
+                            var _ = element._,
+                                eq = equaliseTransform(_[attr], to[attr]);
+                            if (eq) {
+                                from[attr] = eq.from;
+                                to[attr] = eq.to;
+                                diff[attr] = [];
+                                diff[attr].real = true;
+                                for (i = 0, ii = from[attr].length; i < ii; i++) {
+                                    diff[attr][i] = [from[attr][i][0]];
+                                    for (j = 1, jj = from[attr][i].length; j < jj; j++) {
+                                        diff[attr][i][j] = (to[attr][i][j] - from[attr][i][j]) / ms;
+                                    }
+                                }
+                            } else {
+                                var m = (element.matrix || new Matrix),
+                                    to2 = {
+                                        _: {transform: _.transform},
+                                        getBBox: function () {
+                                            return element.getBBox(1);
+                                        }
+                                    };
+                                from[attr] = [
+                                    m.a,
+                                    m.b,
+                                    m.c,
+                                    m.d,
+                                    m.e,
+                                    m.f
+                                ];
+                                extractTransform(to2, to[attr]);
+                                to[attr] = to2._.transform;
+                                diff[attr] = [
+                                    (to2.matrix.a - m.a) / ms,
+                                    (to2.matrix.b - m.b) / ms,
+                                    (to2.matrix.c - m.c) / ms,
+                                    (to2.matrix.d - m.d) / ms,
+                                    (to2.matrix.e - m.e) / ms,
+                                    (to2.matrix.f - m.f) / ms
+                                ];
+                                // from[attr] = [_.sx, _.sy, _.deg, _.dx, _.dy];
+                                // var to2 = {_:{}, getBBox: function () { return element.getBBox(); }};
+                                // extractTransform(to2, to[attr]);
+                                // diff[attr] = [
+                                //     (to2._.sx - _.sx) / ms,
+                                //     (to2._.sy - _.sy) / ms,
+                                //     (to2._.deg - _.deg) / ms,
+                                //     (to2._.dx - _.dx) / ms,
+                                //     (to2._.dy - _.dy) / ms
+                                // ];
+                            }
+                            break;
+                        case "csv":
+                            var values = Str(params[attr])[split](separator),
+                                from2 = Str(from[attr])[split](separator);
+                            if (attr == "clip-rect") {
+                                from[attr] = from2;
+                                diff[attr] = [];
+                                i = from2.length;
+                                while (i--) {
+                                    diff[attr][i] = (values[i] - from[attr][i]) / ms;
+                                }
+                            }
+                            to[attr] = values;
+                            break;
+                        default:
+                            values = [][concat](params[attr]);
+                            from2 = [][concat](from[attr]);
+                            diff[attr] = [];
+                            i = element.paper.customAttributes[attr].length;
+                            while (i--) {
+                                diff[attr][i] = ((values[i] || 0) - (from2[i] || 0)) / ms;
+                            }
+                            break;
+                    }
+                }
+            }
+            var easing = params.easing,
+                easyeasy = R.easing_formulas[easing];
+            if (!easyeasy) {
+                easyeasy = Str(easing).match(bezierrg);
+                if (easyeasy && easyeasy.length == 5) {
+                    var curve = easyeasy;
+                    easyeasy = function (t) {
+                        return CubicBezierAtTime(t, +curve[1], +curve[2], +curve[3], +curve[4], ms);
+                    };
+                } else {
+                    easyeasy = pipe;
+                }
+            }
+            timestamp = params.start || anim.start || +new Date;
+            e = {
+                anim: anim,
+                percent: percent,
+                timestamp: timestamp,
+                start: timestamp + (anim.del || 0),
+                status: 0,
+                initstatus: status || 0,
+                stop: false,
+                ms: ms,
+                easing: easyeasy,
+                from: from,
+                diff: diff,
+                to: to,
+                el: element,
+                callback: params.callback,
+                prev: prev,
+                next: next,
+                repeat: times || anim.times,
+                origin: element.attr(),
+                totalOrigin: totalOrigin
+            };
+            animationElements.push(e);
+            if (status && !isInAnim && !isInAnimSet) {
+                e.stop = true;
+                e.start = new Date - ms * status;
+                if (animationElements.length == 1) {
+                    return animation();
+                }
+            }
+            if (isInAnimSet) {
+                e.start = new Date - e.ms * status;
+            }
+            animationElements.length == 1 && requestAnimFrame(animation);
+        } else {
+            isInAnim.initstatus = status;
+            isInAnim.start = new Date - isInAnim.ms * status;
+        }
+        eve("raphael.anim.start." + element.id, element, anim);
+    }
+    /*\
+     * Raphael.animation
+     [ method ]
+     **
+     * Creates an animation object that can be passed to the @Element.animate or @Element.animateWith methods.
+     * See also @Animation.delay and @Animation.repeat methods.
+     **
+     > Parameters
+     **
+     - params (object) final attributes for the element, see also @Element.attr
+     - ms (number) number of milliseconds for animation to run
+     - easing (string) #optional easing type. Accept one of @Raphael.easing_formulas or CSS format: `cubic&#x2010;bezier(XX,&#160;XX,&#160;XX,&#160;XX)`
+     - callback (function) #optional callback function. Will be called at the end of animation.
+     **
+     = (object) @Animation
+    \*/
+    R.animation = function (params, ms, easing, callback) {
+        if (params instanceof Animation) {
+            return params;
+        }
+        if (R.is(easing, "function") || !easing) {
+            callback = callback || easing || null;
+            easing = null;
+        }
+        params = Object(params);
+        ms = +ms || 0;
+        var p = {},
+            json,
+            attr;
+        for (attr in params) if (params[has](attr) && toFloat(attr) != attr && toFloat(attr) + "%" != attr) {
+            json = true;
+            p[attr] = params[attr];
+        }
+        if (!json) {
+            // if percent-like syntax is used and end-of-all animation callback used
+            if(callback){
+                // find the last one
+                var lastKey = 0;
+                for(var i in params){
+                    var percent = toInt(i);
+                    if(params[has](i) && percent > lastKey){
+                        lastKey = percent;
+                    }
+                }
+                lastKey += '%';
+                // if already defined callback in the last keyframe, skip
+                !params[lastKey].callback && (params[lastKey].callback = callback);
+            }
+          return new Animation(params, ms);
+        } else {
+            easing && (p.easing = easing);
+            callback && (p.callback = callback);
+            return new Animation({100: p}, ms);
+        }
+    };
+    /*\
+     * Element.animate
+     [ method ]
+     **
+     * Creates and starts animation for given element.
+     **
+     > Parameters
+     **
+     - params (object) final attributes for the element, see also @Element.attr
+     - ms (number) number of milliseconds for animation to run
+     - easing (string) #optional easing type. Accept one of @Raphael.easing_formulas or CSS format: `cubic&#x2010;bezier(XX,&#160;XX,&#160;XX,&#160;XX)`
+     - callback (function) #optional callback function. Will be called at the end of animation.
+     * or
+     - animation (object) animation object, see @Raphael.animation
+     **
+     = (object) original element
+    \*/
+    elproto.animate = function (params, ms, easing, callback) {
+        var element = this;
+        if (element.removed) {
+            callback && callback.call(element);
+            return element;
+        }
+        var anim = params instanceof Animation ? params : R.animation(params, ms, easing, callback);
+        runAnimation(anim, element, anim.percents[0], null, element.attr());
+        return element;
+    };
+    /*\
+     * Element.setTime
+     [ method ]
+     **
+     * Sets the status of animation of the element in milliseconds. Similar to @Element.status method.
+     **
+     > Parameters
+     **
+     - anim (object) animation object
+     - value (number) number of milliseconds from the beginning of the animation
+     **
+     = (object) original element if `value` is specified
+     * Note, that during animation following events are triggered:
+     *
+     * On each animation frame event `anim.frame.<id>`, on start `anim.start.<id>` and on end `anim.finish.<id>`.
+    \*/
+    elproto.setTime = function (anim, value) {
+        if (anim && value != null) {
+            this.status(anim, mmin(value, anim.ms) / anim.ms);
+        }
+        return this;
+    };
+    /*\
+     * Element.status
+     [ method ]
+     **
+     * Gets or sets the status of animation of the element.
+     **
+     > Parameters
+     **
+     - anim (object) #optional animation object
+     - value (number) #optional 0 – 1. If specified, method works like a setter and sets the status of a given animation to the value. This will cause animation to jump to the given position.
+     **
+     = (number) status
+     * or
+     = (array) status if `anim` is not specified. Array of objects in format:
+     o {
+     o     anim: (object) animation object
+     o     status: (number) status
+     o }
+     * or
+     = (object) original element if `value` is specified
+    \*/
+    elproto.status = function (anim, value) {
+        var out = [],
+            i = 0,
+            len,
+            e;
+        if (value != null) {
+            runAnimation(anim, this, -1, mmin(value, 1));
+            return this;
+        } else {
+            len = animationElements.length;
+            for (; i < len; i++) {
+                e = animationElements[i];
+                if (e.el.id == this.id && (!anim || e.anim == anim)) {
+                    if (anim) {
+                        return e.status;
+                    }
+                    out.push({
+                        anim: e.anim,
+                        status: e.status
+                    });
+                }
+            }
+            if (anim) {
+                return 0;
+            }
+            return out;
+        }
+    };
+    /*\
+     * Element.pause
+     [ method ]
+     **
+     * Stops animation of the element with ability to resume it later on.
+     **
+     > Parameters
+     **
+     - anim (object) #optional animation object
+     **
+     = (object) original element
+    \*/
+    elproto.pause = function (anim) {
+        for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
+            if (eve("raphael.anim.pause." + this.id, this, animationElements[i].anim) !== false) {
+                animationElements[i].paused = true;
+            }
+        }
+        return this;
+    };
+    /*\
+     * Element.resume
+     [ method ]
+     **
+     * Resumes animation if it was paused with @Element.pause method.
+     **
+     > Parameters
+     **
+     - anim (object) #optional animation object
+     **
+     = (object) original element
+    \*/
+    elproto.resume = function (anim) {
+        for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
+            var e = animationElements[i];
+            if (eve("raphael.anim.resume." + this.id, this, e.anim) !== false) {
+                delete e.paused;
+                this.status(e.anim, e.status);
+            }
+        }
+        return this;
+    };
+    /*\
+     * Element.stop
+     [ method ]
+     **
+     * Stops animation of the element.
+     **
+     > Parameters
+     **
+     - anim (object) #optional animation object
+     **
+     = (object) original element
+    \*/
+    elproto.stop = function (anim) {
+        for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
+            if (eve("raphael.anim.stop." + this.id, this, animationElements[i].anim) !== false) {
+                animationElements.splice(i--, 1);
+            }
+        }
+        return this;
+    };
+    function stopAnimation(paper) {
+        for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.paper == paper) {
+            animationElements.splice(i--, 1);
+        }
+    }
+    eve.on("raphael.remove", stopAnimation);
+    eve.on("raphael.clear", stopAnimation);
+    elproto.toString = function () {
+        return "Rapha\xebl\u2019s object";
+    };
+
+    // Set
+    var Set = function (items) {
+        this.items = [];
+        this.length = 0;
+        this.type = "set";
+        if (items) {
+            for (var i = 0, ii = items.length; i < ii; i++) {
+                if (items[i] && (items[i].constructor == elproto.constructor || items[i].constructor == Set)) {
+                    this[this.items.length] = this.items[this.items.length] = items[i];
+                    this.length++;
+                }
+            }
+        }
+    },
+    setproto = Set.prototype;
+    /*\
+     * Set.push
+     [ method ]
+     **
+     * Adds each argument to the current set.
+     = (object) original element
+    \*/
+    setproto.push = function () {
+        var item,
+            len;
+        for (var i = 0, ii = arguments.length; i < ii; i++) {
+            item = arguments[i];
+            if (item && (item.constructor == elproto.constructor || item.constructor == Set)) {
+                len = this.items.length;
+                this[len] = this.items[len] = item;
+                this.length++;
+            }
+        }
+        return this;
+    };
+    /*\
+     * Set.pop
+     [ method ]
+     **
+     * Removes last element and returns it.
+     = (object) element
+    \*/
+    setproto.pop = function () {
+        this.length && delete this[this.length--];
+        return this.items.pop();
+    };
+    /*\
+     * Set.forEach
+     [ method ]
+     **
+     * Executes given function for each element in the set.
+     *
+     * If function returns `false` it will stop loop running.
+     **
+     > Parameters
+     **
+     - callback (function) function to run
+     - thisArg (object) context object for the callback
+     = (object) Set object
+    \*/
+    setproto.forEach = function (callback, thisArg) {
+        for (var i = 0, ii = this.items.length; i < ii; i++) {
+            if (callback.call(thisArg, this.items[i], i) === false) {
+                return this;
+            }
+        }
+        return this;
+    };
+    for (var method in elproto) if (elproto[has](method)) {
+        setproto[method] = (function (methodname) {
+            return function () {
+                var arg = arguments;
+                return this.forEach(function (el) {
+                    el[methodname][apply](el, arg);
+                });
+            };
+        })(method);
+    }
+    setproto.attr = function (name, value) {
+        if (name && R.is(name, array) && R.is(name[0], "object")) {
+            for (var j = 0, jj = name.length; j < jj; j++) {
+                this.items[j].attr(name[j]);
+            }
+        } else {
+            for (var i = 0, ii = this.items.length; i < ii; i++) {
+                this.items[i].attr(name, value);
+            }
+        }
+        return this;
+    };
+    /*\
+     * Set.clear
+     [ method ]
+     **
+     * Removes all elements from the set
+    \*/
+    setproto.clear = function () {
+        while (this.length) {
+            this.pop();
+        }
+    };
+    /*\
+     * Set.splice
+     [ method ]
+     **
+     * Removes given element from the set
+     **
+     > Parameters
+     **
+     - index (number) position of the deletion
+     - count (number) number of element to remove
+     - insertion… (object) #optional elements to insert
+     = (object) set elements that were deleted
+    \*/
+    setproto.splice = function (index, count, insertion) {
+        index = index < 0 ? mmax(this.length + index, 0) : index;
+        count = mmax(0, mmin(this.length - index, count));
+        var tail = [],
+            todel = [],
+            args = [],
+            i;
+        for (i = 2; i < arguments.length; i++) {
+            args.push(arguments[i]);
+        }
+        for (i = 0; i < count; i++) {
+            todel.push(this[index + i]);
+        }
+        for (; i < this.length - index; i++) {
+            tail.push(this[index + i]);
+        }
+        var arglen = args.length;
+        for (i = 0; i < arglen + tail.length; i++) {
+            this.items[index + i] = this[index + i] = i < arglen ? args[i] : tail[i - arglen];
+        }
+        i = this.items.length = this.length -= count - arglen;
+        while (this[i]) {
+            delete this[i++];
+        }
+        return new Set(todel);
+    };
+    /*\
+     * Set.exclude
+     [ method ]
+     **
+     * Removes given element from the set
+     **
+     > Parameters
+     **
+     - element (object) element to remove
+     = (boolean) `true` if object was found & removed from the set
+    \*/
+    setproto.exclude = function (el) {
+        for (var i = 0, ii = this.length; i < ii; i++) if (this[i] == el) {
+            this.splice(i, 1);
+            return true;
+        }
+    };
+    setproto.animate = function (params, ms, easing, callback) {
+        (R.is(easing, "function") || !easing) && (callback = easing || null);
+        var len = this.items.length,
+            i = len,
+            item,
+            set = this,
+            collector;
+        if (!len) {
+            return this;
+        }
+        callback && (collector = function () {
+            !--len && callback.call(set);
+        });
+        easing = R.is(easing, string) ? easing : collector;
+        var anim = R.animation(params, ms, easing, collector);
+        item = this.items[--i].animate(anim);
+        while (i--) {
+            this.items[i] && !this.items[i].removed && this.items[i].animateWith(item, anim, anim);
+            (this.items[i] && !this.items[i].removed) || len--;
+        }
+        return this;
+    };
+    setproto.insertAfter = function (el) {
+        var i = this.items.length;
+        while (i--) {
+            this.items[i].insertAfter(el);
+        }
+        return this;
+    };
+    setproto.getBBox = function () {
+        var x = [],
+            y = [],
+            x2 = [],
+            y2 = [];
+        for (var i = this.items.length; i--;) if (!this.items[i].removed) {
+            var box = this.items[i].getBBox();
+            x.push(box.x);
+            y.push(box.y);
+            x2.push(box.x + box.width);
+            y2.push(box.y + box.height);
+        }
+        x = mmin[apply](0, x);
+        y = mmin[apply](0, y);
+        x2 = mmax[apply](0, x2);
+        y2 = mmax[apply](0, y2);
+        return {
+            x: x,
+            y: y,
+            x2: x2,
+            y2: y2,
+            width: x2 - x,
+            height: y2 - y
+        };
+    };
+    setproto.clone = function (s) {
+        s = this.paper.set();
+        for (var i = 0, ii = this.items.length; i < ii; i++) {
+            s.push(this.items[i].clone());
+        }
+        return s;
+    };
+    setproto.toString = function () {
+        return "Rapha\xebl\u2018s set";
+    };
+
+    setproto.glow = function(glowConfig) {
+        var ret = this.paper.set();
+        this.forEach(function(shape, index){
+            var g = shape.glow(glowConfig);
+            if(g != null){
+                g.forEach(function(shape2, index2){
+                    ret.push(shape2);
+                });
+            }
+        });
+        return ret;
+    };
+
+
+    /*\
+     * Set.isPointInside
+     [ method ]
+     **
+     * Determine if given point is inside this set’s elements
+     **
+     > Parameters
+     **
+     - x (number) x coordinate of the point
+     - y (number) y coordinate of the point
+     = (boolean) `true` if point is inside any of the set's elements
+     \*/
+    setproto.isPointInside = function (x, y) {
+        var isPointInside = false;
+        this.forEach(function (el) {
+            if (el.isPointInside(x, y)) {
+                isPointInside = true;
+                return false; // stop loop
+            }
+        });
+        return isPointInside;
+    };
+
+    /*\
+     * Raphael.registerFont
+     [ method ]
+     **
+     * Adds given font to the registered set of fonts for Raphaël. Should be used as an internal call from within Cufón’s font file.
+     * Returns original parameter, so it could be used with chaining.
+     # <a href="http://wiki.github.com/sorccu/cufon/about">More about Cufón and how to convert your font form TTF, OTF, etc to JavaScript file.</a>
+     **
+     > Parameters
+     **
+     - font (object) the font to register
+     = (object) the font you passed in
+     > Usage
+     | Cufon.registerFont(Raphael.registerFont({…}));
+    \*/
+    R.registerFont = function (font) {
+        if (!font.face) {
+            return font;
+        }
+        this.fonts = this.fonts || {};
+        var fontcopy = {
+                w: font.w,
+                face: {},
+                glyphs: {}
+            },
+            family = font.face["font-family"];
+        for (var prop in font.face) if (font.face[has](prop)) {
+            fontcopy.face[prop] = font.face[prop];
+        }
+        if (this.fonts[family]) {
+            this.fonts[family].push(fontcopy);
+        } else {
+            this.fonts[family] = [fontcopy];
+        }
+        if (!font.svg) {
+            fontcopy.face["units-per-em"] = toInt(font.face["units-per-em"], 10);
+            for (var glyph in font.glyphs) if (font.glyphs[has](glyph)) {
+                var path = font.glyphs[glyph];
+                fontcopy.glyphs[glyph] = {
+                    w: path.w,
+                    k: {},
+                    d: path.d && "M" + path.d.replace(/[mlcxtrv]/g, function (command) {
+                            return {l: "L", c: "C", x: "z", t: "m", r: "l", v: "c"}[command] || "M";
+                        }) + "z"
+                };
+                if (path.k) {
+                    for (var k in path.k) if (path[has](k)) {
+                        fontcopy.glyphs[glyph].k[k] = path.k[k];
+                    }
+                }
+            }
+        }
+        return font;
+    };
+    /*\
+     * Paper.getFont
+     [ method ]
+     **
+     * Finds font object in the registered fonts by given parameters. You could specify only one word from the font name, like “Myriad” for “Myriad Pro”.
+     **
+     > Parameters
+     **
+     - family (string) font family name or any word from it
+     - weight (string) #optional font weight
+     - style (string) #optional font style
+     - stretch (string) #optional font stretch
+     = (object) the font object
+     > Usage
+     | paper.print(100, 100, "Test string", paper.getFont("Times", 800), 30);
+    \*/
+    paperproto.getFont = function (family, weight, style, stretch) {
+        stretch = stretch || "normal";
+        style = style || "normal";
+        weight = +weight || {normal: 400, bold: 700, lighter: 300, bolder: 800}[weight] || 400;
+        if (!R.fonts) {
+            return;
+        }
+        var font = R.fonts[family];
+        if (!font) {
+            var name = new RegExp("(^|\\s)" + family.replace(/[^\w\d\s+!~.:_-]/g, E) + "(\\s|$)", "i");
+            for (var fontName in R.fonts) if (R.fonts[has](fontName)) {
+                if (name.test(fontName)) {
+                    font = R.fonts[fontName];
+                    break;
+                }
+            }
+        }
+        var thefont;
+        if (font) {
+            for (var i = 0, ii = font.length; i < ii; i++) {
+                thefont = font[i];
+                if (thefont.face["font-weight"] == weight && (thefont.face["font-style"] == style || !thefont.face["font-style"]) && thefont.face["font-stretch"] == stretch) {
+                    break;
+                }
+            }
+        }
+        return thefont;
+    };
+    /*\
+     * Paper.print
+     [ method ]
+     **
+     * Creates path that represent given text written using given font at given position with given size.
+     * Result of the method is path element that contains whole text as a separate path.
+     **
+     > Parameters
+     **
+     - x (number) x position of the text
+     - y (number) y position of the text
+     - string (string) text to print
+     - font (object) font object, see @Paper.getFont
+     - size (number) #optional size of the font, default is `16`
+     - origin (string) #optional could be `"baseline"` or `"middle"`, default is `"middle"`
+     - letter_spacing (number) #optional number in range `-1..1`, default is `0`
+     - line_spacing (number) #optional number in range `1..3`, default is `1`
+     = (object) resulting path element, which consist of all letters
+     > Usage
+     | var txt = r.print(10, 50, "print", r.getFont("Museo"), 30).attr({fill: "#fff"});
+    \*/
+    paperproto.print = function (x, y, string, font, size, origin, letter_spacing, line_spacing) {
+        origin = origin || "middle"; // baseline|middle
+        letter_spacing = mmax(mmin(letter_spacing || 0, 1), -1);
+        line_spacing = mmax(mmin(line_spacing || 1, 3), 1);
+        var letters = Str(string)[split](E),
+            shift = 0,
+            notfirst = 0,
+            path = E,
+            scale;
+        R.is(font, "string") && (font = this.getFont(font));
+        if (font) {
+            scale = (size || 16) / font.face["units-per-em"];
+            var bb = font.face.bbox[split](separator),
+                top = +bb[0],
+                lineHeight = bb[3] - bb[1],
+                shifty = 0,
+                height = +bb[1] + (origin == "baseline" ? lineHeight + (+font.face.descent) : lineHeight / 2);
+            for (var i = 0, ii = letters.length; i < ii; i++) {
+                if (letters[i] == "\n") {
+                    shift = 0;
+                    curr = 0;
+                    notfirst = 0;
+                    shifty += lineHeight * line_spacing;
+                } else {
+                    var prev = notfirst && font.glyphs[letters[i - 1]] || {},
+                        curr = font.glyphs[letters[i]];
+                    shift += notfirst ? (prev.w || font.w) + (prev.k && prev.k[letters[i]] || 0) + (font.w * letter_spacing) : 0;
+                    notfirst = 1;
+                }
+                if (curr && curr.d) {
+                    path += R.transformPath(curr.d, ["t", shift * scale, shifty * scale, "s", scale, scale, top, height, "t", (x - top) / scale, (y - height) / scale]);
+                }
+            }
+        }
+        return this.path(path).attr({
+            fill: "#000",
+            stroke: "none"
+        });
+    };
+
+    /*\
+     * Paper.add
+     [ method ]
+     **
+     * Imports elements in JSON array in format `{type: type, <attributes>}`
+     **
+     > Parameters
+     **
+     - json (array)
+     = (object) resulting set of imported elements
+     > Usage
+     | paper.add([
+     |     {
+     |         type: "circle",
+     |         cx: 10,
+     |         cy: 10,
+     |         r: 5
+     |     },
+     |     {
+     |         type: "rect",
+     |         x: 10,
+     |         y: 10,
+     |         width: 10,
+     |         height: 10,
+     |         fill: "#fc0"
+     |     }
+     | ]);
+    \*/
+    paperproto.add = function (json) {
+        if (R.is(json, "array")) {
+            var res = this.set(),
+                i = 0,
+                ii = json.length,
+                j;
+            for (; i < ii; i++) {
+                j = json[i] || {};
+                elements[has](j.type) && res.push(this[j.type]().attr(j));
+            }
+        }
+        return res;
+    };
+
+    /*\
+     * Raphael.format
+     [ method ]
+     **
+     * Simple format function. Replaces construction of type “`{<number>}`” to the corresponding argument.
+     **
+     > Parameters
+     **
+     - token (string) string to format
+     - … (string) rest of arguments will be treated as parameters for replacement
+     = (string) formated string
+     > Usage
+     | var x = 10,
+     |     y = 20,
+     |     width = 40,
+     |     height = 50;
+     | // this will draw a rectangular shape equivalent to "M10,20h40v50h-40z"
+     | paper.path(Raphael.format("M{0},{1}h{2}v{3}h{4}z", x, y, width, height, -width));
+    \*/
+    R.format = function (token, params) {
+        var args = R.is(params, array) ? [0][concat](params) : arguments;
+        token && R.is(token, string) && args.length - 1 && (token = token.replace(formatrg, function (str, i) {
+            return args[++i] == null ? E : args[i];
+        }));
+        return token || E;
+    };
+    /*\
+     * Raphael.fullfill
+     [ method ]
+     **
+     * A little bit more advanced format function than @Raphael.format. Replaces construction of type “`{<name>}`” to the corresponding argument.
+     **
+     > Parameters
+     **
+     - token (string) string to format
+     - json (object) object which properties will be used as a replacement
+     = (string) formated string
+     > Usage
+     | // this will draw a rectangular shape equivalent to "M10,20h40v50h-40z"
+     | paper.path(Raphael.fullfill("M{x},{y}h{dim.width}v{dim.height}h{dim['negative width']}z", {
+     |     x: 10,
+     |     y: 20,
+     |     dim: {
+     |         width: 40,
+     |         height: 50,
+     |         "negative width": -40
+     |     }
+     | }));
+    \*/
+    R.fullfill = (function () {
+        var tokenRegex = /\{([^\}]+)\}/g,
+            objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g, // matches .xxxxx or ["xxxxx"] to run over object properties
+            replacer = function (all, key, obj) {
+                var res = obj;
+                key.replace(objNotationRegex, function (all, name, quote, quotedName, isFunc) {
+                    name = name || quotedName;
+                    if (res) {
+                        if (name in res) {
+                            res = res[name];
+                        }
+                        typeof res == "function" && isFunc && (res = res());
+                    }
+                });
+                res = (res == null || res == obj ? all : res) + "";
+                return res;
+            };
+        return function (str, obj) {
+            return String(str).replace(tokenRegex, function (all, key) {
+                return replacer(all, key, obj);
+            });
+        };
+    })();
+    /*\
+     * Raphael.ninja
+     [ method ]
+     **
+     * If you want to leave no trace of Raphaël (Well, Raphaël creates only one global variable `Raphael`, but anyway.) You can use `ninja` method.
+     * Beware, that in this case plugins could stop working, because they are depending on global variable existance.
+     **
+     = (object) Raphael object
+     > Usage
+     | (function (local_raphael) {
+     |     var paper = local_raphael(10, 10, 320, 200);
+     |     …
+     | })(Raphael.ninja());
+    \*/
+    R.ninja = function () {
+        oldRaphael.was ? (g.win.Raphael = oldRaphael.is) : delete Raphael;
+        return R;
+    };
+    /*\
+     * Raphael.st
+     [ property (object) ]
+     **
+     * You can add your own method to elements and sets. It is wise to add a set method for each element method
+     * you added, so you will be able to call the same method on sets too.
+     **
+     * See also @Raphael.el.
+     > Usage
+     | Raphael.el.red = function () {
+     |     this.attr({fill: "#f00"});
+     | };
+     | Raphael.st.red = function () {
+     |     this.forEach(function (el) {
+     |         el.red();
+     |     });
+     | };
+     | // then use it
+     | paper.set(paper.circle(100, 100, 20), paper.circle(110, 100, 20)).red();
+    \*/
+    R.st = setproto;
+
+    eve.on("raphael.DOMload", function () {
+        loaded = true;
+    });
+
+    // Firefox <3.6 fix: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html
+    (function (doc, loaded, f) {
+        if (doc.readyState == null && doc.addEventListener){
+            doc.addEventListener(loaded, f = function () {
+                doc.removeEventListener(loaded, f, false);
+                doc.readyState = "complete";
+            }, false);
+            doc.readyState = "loading";
+        }
+        function isLoaded() {
+            (/in/).test(doc.readyState) ? setTimeout(isLoaded, 9) : R.eve("raphael.DOMload");
+        }
+        isLoaded();
+    })(document, "DOMContentLoaded");
+
+// ┌─────────────────────────────────────────────────────────────────────┐ \\
+// │ Raphaël - JavaScript Vector Library                                 │ \\
+// ├─────────────────────────────────────────────────────────────────────┤ \\
+// │ SVG Module                                                          │ \\
+// ├─────────────────────────────────────────────────────────────────────┤ \\
+// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com)   │ \\
+// │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com)             │ \\
+// │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
+// └─────────────────────────────────────────────────────────────────────┘ \\
+
+(function(){
+    if (!R.svg) {
+        return;
+    }
+    var has = "hasOwnProperty",
+        Str = String,
+        toFloat = parseFloat,
+        toInt = parseInt,
+        math = Math,
+        mmax = math.max,
+        abs = math.abs,
+        pow = math.pow,
+        separator = /[, ]+/,
+        eve = R.eve,
+        E = "",
+        S = " ";
+    var xlink = "http://www.w3.org/1999/xlink",
+        markers = {
+            block: "M5,0 0,2.5 5,5z",
+            classic: "M5,0 0,2.5 5,5 3.5,3 3.5,2z",
+            diamond: "M2.5,0 5,2.5 2.5,5 0,2.5z",
+            open: "M6,1 1,3.5 6,6",
+            oval: "M2.5,0A2.5,2.5,0,0,1,2.5,5 2.5,2.5,0,0,1,2.5,0z"
+        },
+        markerCounter = {};
+    R.toString = function () {
+        return  "Your browser supports SVG.\nYou are running Rapha\xebl " + this.version;
+    };
+    var $ = function (el, attr) {
+        if (attr) {
+            if (typeof el == "string") {
+                el = $(el);
+            }
+            for (var key in attr) if (attr[has](key)) {
+                if (key.substring(0, 6) == "xlink:") {
+                    el.setAttributeNS(xlink, key.substring(6), Str(attr[key]));
+                } else {
+                    el.setAttribute(key, Str(attr[key]));
+                }
+            }
+        } else {
+            el = R._g.doc.createElementNS("http://www.w3.org/2000/svg", el);
+            el.style && (el.style.webkitTapHighlightColor = "rgba(0,0,0,0)");
+        }
+        return el;
+    },
+    addGradientFill = function (element, gradient) {
+        var type = "linear",
+            id = element.id + gradient,
+            fx = .5, fy = .5,
+            o = element.node,
+            SVG = element.paper,
+            s = o.style,
+            el = R._g.doc.getElementById(id);
+        if (!el) {
+            gradient = Str(gradient).replace(R._radial_gradient, function (all, _fx, _fy) {
+                type = "radial";
+                if (_fx && _fy) {
+                    fx = toFloat(_fx);
+                    fy = toFloat(_fy);
+                    var dir = ((fy > .5) * 2 - 1);
+                    pow(fx - .5, 2) + pow(fy - .5, 2) > .25 &&
+                        (fy = math.sqrt(.25 - pow(fx - .5, 2)) * dir + .5) &&
+                        fy != .5 &&
+                        (fy = fy.toFixed(5) - 1e-5 * dir);
+                }
+                return E;
+            });
+            gradient = gradient.split(/\s*\-\s*/);
+            if (type == "linear") {
+                var angle = gradient.shift();
+                angle = -toFloat(angle);
+                if (isNaN(angle)) {
+                    return null;
+                }
+                var vector = [0, 0, math.cos(R.rad(angle)), math.sin(R.rad(angle))],
+                    max = 1 / (mmax(abs(vector[2]), abs(vector[3])) || 1);
+                vector[2] *= max;
+                vector[3] *= max;
+                if (vector[2] < 0) {
+                    vector[0] = -vector[2];
+                    vector[2] = 0;
+                }
+                if (vector[3] < 0) {
+                    vector[1] = -vector[3];
+                    vector[3] = 0;
+                }
+            }
+            var dots = R._parseDots(gradient);
+            if (!dots) {
+                return null;
+            }
+            id = id.replace(/[\(\)\s,\xb0#]/g, "_");
+
+            if (element.gradient && id != element.gradient.id) {
+                SVG.defs.removeChild(element.gradient);
+                delete element.gradient;
+            }
+
+            if (!element.gradient) {
+                el = $(type + "Gradient", {id: id});
+                element.gradient = el;
+                $(el, type == "radial" ? {
+                    fx: fx,
+                    fy: fy
+                } : {
+                    x1: vector[0],
+                    y1: vector[1],
+                    x2: vector[2],
+                    y2: vector[3],
+                    gradientTransform: element.matrix.invert()
+                });
+                SVG.defs.appendChild(el);
+                for (var i = 0, ii = dots.length; i < ii; i++) {
+                    el.appendChild($("stop", {
+                        offset: dots[i].offset ? dots[i].offset : i ? "100%" : "0%",
+                        "stop-color": dots[i].color || "#fff"
+                    }));
+                }
+            }
+        }
+        $(o, {
+            fill: "url('" + document.location + "#" + id + "')",
+            opacity: 1,
+            "fill-opacity": 1
+        });
+        s.fill = E;
+        s.opacity = 1;
+        s.fillOpacity = 1;
+        return 1;
+    },
+    updatePosition = function (o) {
+        var bbox = o.getBBox(1);
+        $(o.pattern, {patternTransform: o.matrix.invert() + " translate(" + bbox.x + "," + bbox.y + ")"});
+    },
+    addArrow = function (o, value, isEnd) {
+        if (o.type == "path") {
+            var values = Str(value).toLowerCase().split("-"),
+                p = o.paper,
+                se = isEnd ? "end" : "start",
+                node = o.node,
+                attrs = o.attrs,
+                stroke = attrs["stroke-width"],
+                i = values.length,
+                type = "classic",
+                from,
+                to,
+                dx,
+                refX,
+                attr,
+                w = 3,
+                h = 3,
+                t = 5;
+            while (i--) {
+                switch (values[i]) {
+                    case "block":
+                    case "classic":
+                    case "oval":
+                    case "diamond":
+                    case "open":
+                    case "none":
+                        type = values[i];
+                        break;
+                    case "wide": h = 5; break;
+                    case "narrow": h = 2; break;
+                    case "long": w = 5; break;
+                    case "short": w = 2; break;
+                }
+            }
+            if (type == "open") {
+                w += 2;
+                h += 2;
+                t += 2;
+                dx = 1;
+                refX = isEnd ? 4 : 1;
+                attr = {
+                    fill: "none",
+                    stroke: attrs.stroke
+                };
+            } else {
+                refX = dx = w / 2;
+                attr = {
+                    fill: attrs.stroke,
+                    stroke: "none"
+                };
+            }
+            if (o._.arrows) {
+                if (isEnd) {
+                    o._.arrows.endPath && markerCounter[o._.arrows.endPath]--;
+                    o._.arrows.endMarker && markerCounter[o._.arrows.endMarker]--;
+                } else {
+                    o._.arrows.startPath && markerCounter[o._.arrows.startPath]--;
+                    o._.arrows.startMarker && markerCounter[o._.arrows.startMarker]--;
+                }
+            } else {
+                o._.arrows = {};
+            }
+            if (type != "none") {
+                var pathId = "raphael-marker-" + type,
+                    markerId = "raphael-marker-" + se + type + w + h + "-obj" + o.id;
+                if (!R._g.doc.getElementById(pathId)) {
+                    p.defs.appendChild($($("path"), {
+                        "stroke-linecap": "round",
+                        d: markers[type],
+                        id: pathId
+                    }));
+                    markerCounter[pathId] = 1;
+                } else {
+                    markerCounter[pathId]++;
+                }
+                var marker = R._g.doc.getElementById(markerId),
+                    use;
+                if (!marker) {
+                    marker = $($("marker"), {
+                        id: markerId,
+                        markerHeight: h,
+                        markerWidth: w,
+                        orient: "auto",
+                        refX: refX,
+                        refY: h / 2
+                    });
+                    use = $($("use"), {
+                        "xlink:href": "#" + pathId,
+                        transform: (isEnd ? "rotate(180 " + w / 2 + " " + h / 2 + ") " : E) + "scale(" + w / t + "," + h / t + ")",
+                        "stroke-width": (1 / ((w / t + h / t) / 2)).toFixed(4)
+                    });
+                    marker.appendChild(use);
+                    p.defs.appendChild(marker);
+                    markerCounter[markerId] = 1;
+                } else {
+                    markerCounter[markerId]++;
+                    use = marker.getElementsByTagName("use")[0];
+                }
+                $(use, attr);
+                var delta = dx * (type != "diamond" && type != "oval");
+                if (isEnd) {
+                    from = o._.arrows.startdx * stroke || 0;
+                    to = R.getTotalLength(attrs.path) - delta * stroke;
+                } else {
+                    from = delta * stroke;
+                    to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0);
+                }
+                attr = {};
+                attr["marker-" + se] = "url(#" + markerId + ")";
+                if (to || from) {
+                    attr.d = R.getSubpath(attrs.path, from, to);
+                }
+                $(node, attr);
+                o._.arrows[se + "Path"] = pathId;
+                o._.arrows[se + "Marker"] = markerId;
+                o._.arrows[se + "dx"] = delta;
+                o._.arrows[se + "Type"] = type;
+                o._.arrows[se + "String"] = value;
+            } else {
+                if (isEnd) {
+                    from = o._.arrows.startdx * stroke || 0;
+                    to = R.getTotalLength(attrs.path) - from;
+                } else {
+                    from = 0;
+                    to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0);
+                }
+                o._.arrows[se + "Path"] && $(node, {d: R.getSubpath(attrs.path, from, to)});
+                delete o._.arrows[se + "Path"];
+                delete o._.arrows[se + "Marker"];
+                delete o._.arrows[se + "dx"];
+                delete o._.arrows[se + "Type"];
+                delete o._.arrows[se + "String"];
+            }
+            for (attr in markerCounter) if (markerCounter[has](attr) && !markerCounter[attr]) {
+                var item = R._g.doc.getElementById(attr);
+                item && item.parentNode.removeChild(item);
+            }
+        }
+    },
+    dasharray = {
+        "": [0],
+        "none": [0],
+        "-": [3, 1],
+        ".": [1, 1],
+        "-.": [3, 1, 1, 1],
+        "-..": [3, 1, 1, 1, 1, 1],
+        ". ": [1, 3],
+        "- ": [4, 3],
+        "--": [8, 3],
+        "- .": [4, 3, 1, 3],
+        "--.": [8, 3, 1, 3],
+        "--..": [8, 3, 1, 3, 1, 3]
+    },
+    addDashes = function (o, value, params) {
+        value = dasharray[Str(value).toLowerCase()];
+        if (value) {
+            var width = o.attrs["stroke-width"] || "1",
+                butt = {round: width, square: width, butt: 0}[o.attrs["stroke-linecap"] || params["stroke-linecap"]] || 0,
+                dashes = [],
+                i = value.length;
+            while (i--) {
+                dashes[i] = value[i] * width + ((i % 2) ? 1 : -1) * butt;
+            }
+            $(o.node, {"stroke-dasharray": dashes.join(",")});
+        }
+    },
+    setFillAndStroke = function (o, params) {
+        var node = o.node,
+            attrs = o.attrs,
+            vis = node.style.visibility;
+        node.style.visibility = "hidden";
+        for (var att in params) {
+            if (params[has](att)) {
+                if (!R._availableAttrs[has](att)) {
+                    continue;
+                }
+                var value = params[att];
+                attrs[att] = value;
+                switch (att) {
+                    case "blur":
+                        o.blur(value);
+                        break;
+                    case "title":
+                        var title = node.getElementsByTagName("title");
+
+                        // Use the existing <title>.
+                        if (title.length && (title = title[0])) {
+                          title.firstChild.nodeValue = value;
+                        } else {
+                          title = $("title");
+                          var val = R._g.doc.createTextNode(value);
+                          title.appendChild(val);
+                          node.appendChild(title);
+                        }
+                        break;
+                    case "href":
+                    case "target":
+                        var pn = node.parentNode;
+                        if (pn.tagName.toLowerCase() != "a") {
+                            var hl = $("a");
+                            pn.insertBefore(hl, node);
+                            hl.appendChild(node);
+                            pn = hl;
+                        }
+                        if (att == "target") {
+                            pn.setAttributeNS(xlink, "show", value == "blank" ? "new" : value);
+                        } else {
+                            pn.setAttributeNS(xlink, att, value);
+                        }
+                        break;
+                    case "cursor":
+                        node.style.cursor = value;
+                        break;
+                    case "transform":
+                        o.transform(value);
+                        break;
+                    case "arrow-start":
+                        addArrow(o, value);
+                        break;
+                    case "arrow-end":
+                        addArrow(o, value, 1);
+                        break;
+                    case "clip-rect":
+                        var rect = Str(value).split(separator);
+                        if (rect.length == 4) {
+                            o.clip && o.clip.parentNode.parentNode.removeChild(o.clip.parentNode);
+                            var el = $("clipPath"),
+                                rc = $("rect");
+                            el.id = R.createUUID();
+                            $(rc, {
+                                x: rect[0],
+                                y: rect[1],
+                                width: rect[2],
+                                height: rect[3]
+                            });
+                            el.appendChild(rc);
+                            o.paper.defs.appendChild(el);
+                            $(node, {"clip-path": "url(#" + el.id + ")"});
+                            o.clip = rc;
+                        }
+                        if (!value) {
+                            var path = node.getAttribute("clip-path");
+                            if (path) {
+                                var clip = R._g.doc.getElementById(path.replace(/(^url\(#|\)$)/g, E));
+                                clip && clip.parentNode.removeChild(clip);
+                                $(node, {"clip-path": E});
+                                delete o.clip;
+                            }
+                        }
+                    break;
+                    case "path":
+                        if (o.type == "path") {
+                            $(node, {d: value ? attrs.path = R._pathToAbsolute(value) : "M0,0"});
+                            o._.dirty = 1;
+                            if (o._.arrows) {
+                                "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
+                                "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
+                            }
+                        }
+                        break;
+                    case "width":
+                        node.setAttribute(att, value);
+                        o._.dirty = 1;
+                        if (attrs.fx) {
+                            att = "x";
+                            value = attrs.x;
+                        } else {
+                            break;
+                        }
+                    case "x":
+                        if (attrs.fx) {
+                            value = -attrs.x - (attrs.width || 0);
+                        }
+                    case "rx":
+                        if (att == "rx" && o.type == "rect") {
+                            break;
+                        }
+                    case "cx":
+                        node.setAttribute(att, value);
+                        o.pattern && updatePosition(o);
+                        o._.dirty = 1;
+                        break;
+                    case "height":
+                        node.setAttribute(att, value);
+                        o._.dirty = 1;
+                        if (attrs.fy) {
+                            att = "y";
+                            value = attrs.y;
+                        } else {
+                            break;
+                        }
+                    case "y":
+                        if (attrs.fy) {
+                            value = -attrs.y - (attrs.height || 0);
+                        }
+                    case "ry":
+                        if (att == "ry" && o.type == "rect") {
+                            break;
+                        }
+                    case "cy":
+                        node.setAttribute(att, value);
+                        o.pattern && updatePosition(o);
+                        o._.dirty = 1;
+                        break;
+                    case "r":
+                        if (o.type == "rect") {
+                            $(node, {rx: value, ry: value});
+                        } else {
+                            node.setAttribute(att, value);
+                        }
+                        o._.dirty = 1;
+                        break;
+                    case "src":
+                        if (o.type == "image") {
+                            node.setAttributeNS(xlink, "href", value);
+                        }
+                        break;
+                    case "stroke-width":
+                        if (o._.sx != 1 || o._.sy != 1) {
+                            value /= mmax(abs(o._.sx), abs(o._.sy)) || 1;
+                        }
+                        node.setAttribute(att, value);
+                        if (attrs["stroke-dasharray"]) {
+                            addDashes(o, attrs["stroke-dasharray"], params);
+                        }
+                        if (o._.arrows) {
+                            "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
+                            "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
+                        }
+                        break;
+                    case "stroke-dasharray":
+                        addDashes(o, value, params);
+                        break;
+                    case "fill":
+                        var isURL = Str(value).match(R._ISURL);
+                        if (isURL) {
+                            el = $("pattern");
+                            var ig = $("image");
+                            el.id = R.createUUID();
+                            $(el, {x: 0, y: 0, patternUnits: "userSpaceOnUse", height: 1, width: 1});
+                            $(ig, {x: 0, y: 0, "xlink:href": isURL[1]});
+                            el.appendChild(ig);
+
+                            (function (el) {
+                                R._preload(isURL[1], function () {
+                                    var w = this.offsetWidth,
+                                        h = this.offsetHeight;
+                                    $(el, {width: w, height: h});
+                                    $(ig, {width: w, height: h});
+                                    o.paper.safari();
+                                });
+                            })(el);
+                            o.paper.defs.appendChild(el);
+                            $(node, {fill: "url(#" + el.id + ")"});
+                            o.pattern = el;
+                            o.pattern && updatePosition(o);
+                            break;
+                        }
+                        var clr = R.getRGB(value);
+                        if (!clr.error) {
+                            delete params.gradient;
+                            delete attrs.gradient;
+                            !R.is(attrs.opacity, "undefined") &&
+                                R.is(params.opacity, "undefined") &&
+                                $(node, {opacity: attrs.opacity});
+                            !R.is(attrs["fill-opacity"], "undefined") &&
+                                R.is(params["fill-opacity"], "undefined") &&
+                                $(node, {"fill-opacity": attrs["fill-opacity"]});
+                        } else if ((o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value)) {
+                            if ("opacity" in attrs || "fill-opacity" in attrs) {
+                                var gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E));
+                                if (gradient) {
+                                    var stops = gradient.getElementsByTagName("stop");
+                                    $(stops[stops.length - 1], {"stop-opacity": ("opacity" in attrs ? attrs.opacity : 1) * ("fill-opacity" in attrs ? attrs["fill-opacity"] : 1)});
+                                }
+                            }
+                            attrs.gradient = value;
+                            attrs.fill = "none";
+                            break;
+                        }
+                        clr[has]("opacity") && $(node, {"fill-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity});
+                    case "stroke":
+                        clr = R.getRGB(value);
+                        node.setAttribute(att, clr.hex);
+                        att == "stroke" && clr[has]("opacity") && $(node, {"stroke-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity});
+                        if (att == "stroke" && o._.arrows) {
+                            "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
+                            "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
+                        }
+                        break;
+                    case "gradient":
+                        (o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value);
+                        break;
+                    case "opacity":
+                        if (attrs.gradient && !attrs[has]("stroke-opacity")) {
+                            $(node, {"stroke-opacity": value > 1 ? value / 100 : value});
+                        }
+                        // fall
+                    case "fill-opacity":
+                        if (attrs.gradient) {
+                            gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E));
+                            if (gradient) {
+                                stops = gradient.getElementsByTagName("stop");
+                                $(stops[stops.length - 1], {"stop-opacity": value});
+                            }
+                            break;
+                        }
+                    default:
+                        att == "font-size" && (value = toInt(value, 10) + "px");
+                        var cssrule = att.replace(/(\-.)/g, function (w) {
+                            return w.substring(1).toUpperCase();
+                        });
+                        node.style[cssrule] = value;
+                        o._.dirty = 1;
+                        node.setAttribute(att, value);
+                        break;
+                }
+            }
+        }
+
+        tuneText(o, params);
+        node.style.visibility = vis;
+    },
+    leading = 1.2,
+    tuneText = function (el, params) {
+        if (el.type != "text" || !(params[has]("text") || params[has]("font") || params[has]("font-size") || params[has]("x") || params[has]("y"))) {
+            return;
+        }
+        var a = el.attrs,
+            node = el.node,
+            fontSize = node.firstChild ? toInt(R._g.doc.defaultView.getComputedStyle(node.firstChild, E).getPropertyValue("font-size"), 10) : 10;
+
+        if (params[has]("text")) {
+            a.text = params.text;
+            while (node.firstChild) {
+                node.removeChild(node.firstChild);
+            }
+            var texts = Str(params.text).split("\n"),
+                tspans = [],
+                tspan;
+            for (var i = 0, ii = texts.length; i < ii; i++) {
+                tspan = $("tspan");
+                i && $(tspan, {dy: fontSize * leading, x: a.x});
+                tspan.appendChild(R._g.doc.createTextNode(texts[i]));
+                node.appendChild(tspan);
+                tspans[i] = tspan;
+            }
+        } else {
+            tspans = node.getElementsByTagName("tspan");
+            for (i = 0, ii = tspans.length; i < ii; i++) if (i) {
+                $(tspans[i], {dy: fontSize * leading, x: a.x});
+            } else {
+                $(tspans[0], {dy: 0});
+            }
+        }
+        $(node, {x: a.x, y: a.y});
+        el._.dirty = 1;
+        var bb = el._getBBox(),
+            dif = a.y - (bb.y + bb.height / 2);
+        dif && R.is(dif, "finite") && $(tspans[0], {dy: dif});
+    },
+    getRealNode = function (node) {
+        if (node.parentNode && node.parentNode.tagName.toLowerCase() === "a") {
+            return node.parentNode;
+        } else {
+            return node;
+        }
+    },
+    Element = function (node, svg) {
+        var X = 0,
+            Y = 0;
+        /*\
+         * Element.node
+         [ property (object) ]
+         **
+         * Gives you a reference to the DOM object, so you can assign event handlers or just mess around.
+         **
+         * Note: Don’t mess with it.
+         > Usage
+         | // draw a circle at coordinate 10,10 with radius of 10
+         | var c = paper.circle(10, 10, 10);
+         | c.node.onclick = function () {
+         |     c.attr("fill", "red");
+         | };
+        \*/
+        this[0] = this.node = node;
+        /*\
+         * Element.raphael
+         [ property (object) ]
+         **
+         * Internal reference to @Raphael object. In case it is not available.
+         > Usage
+         | Raphael.el.red = function () {
+         |     var hsb = this.paper.raphael.rgb2hsb(this.attr("fill"));
+         |     hsb.h = 1;
+         |     this.attr({fill: this.paper.raphael.hsb2rgb(hsb).hex});
+         | }
+        \*/
+        node.raphael = true;
+        /*\
+         * Element.id
+         [ property (number) ]
+         **
+         * Unique id of the element. Especially useful when you want to listen to events of the element,
+         * because all events are fired in format `<module>.<action>.<id>`. Also useful for @Paper.getById method.
+        \*/
+        this.id = R._oid++;
+        node.raphaelid = this.id;
+        this.matrix = R.matrix();
+        this.realPath = null;
+        /*\
+         * Element.paper
+         [ property (object) ]
+         **
+         * Internal reference to “paper” where object drawn. Mainly for use in plugins and element extensions.
+         > Usage
+         | Raphael.el.cross = function () {
+         |     this.attr({fill: "red"});
+         |     this.paper.path("M10,10L50,50M50,10L10,50")
+         |         .attr({stroke: "red"});
+         | }
+        \*/
+        this.paper = svg;
+        this.attrs = this.attrs || {};
+        this._ = {
+            transform: [],
+            sx: 1,
+            sy: 1,
+            deg: 0,
+            dx: 0,
+            dy: 0,
+            dirty: 1
+        };
+        !svg.bottom && (svg.bottom = this);
+        /*\
+         * Element.prev
+         [ property (object) ]
+         **
+         * Reference to the previous element in the hierarchy.
+        \*/
+        this.prev = svg.top;
+        svg.top && (svg.top.next = this);
+        svg.top = this;
+        /*\
+         * Element.next
+         [ property (object) ]
+         **
+         * Reference to the next element in the hierarchy.
+        \*/
+        this.next = null;
+    },
+    elproto = R.el;
+
+    Element.prototype = elproto;
+    elproto.constructor = Element;
+
+    R._engine.path = function (pathString, SVG) {
+        var el = $("path");
+        SVG.canvas && SVG.canvas.appendChild(el);
+        var p = new Element(el, SVG);
+        p.type = "path";
+        setFillAndStroke(p, {
+            fill: "none",
+            stroke: "#000",
+            path: pathString
+        });
+        return p;
+    };
+    /*\
+     * Element.rotate
+     [ method ]
+     **
+     * Deprecated! Use @Element.transform instead.
+     * Adds rotation by given angle around given point to the list of
+     * transformations of the element.
+     > Parameters
+     - deg (number) angle in degrees
+     - cx (number) #optional x coordinate of the centre of rotation
+     - cy (number) #optional y coordinate of the centre of rotation
+     * If cx & cy aren’t specified centre of the shape is used as a point of rotation.
+     = (object) @Element
+    \*/
+    elproto.rotate = function (deg, cx, cy) {
+        if (this.removed) {
+            return this;
+        }
+        deg = Str(deg).split(separator);
+        if (deg.length - 1) {
+            cx = toFloat(deg[1]);
+            cy = toFloat(deg[2]);
+        }
+        deg = toFloat(deg[0]);
+        (cy == null) && (cx = cy);
+        if (cx == null || cy == null) {
+            var bbox = this.getBBox(1);
+            cx = bbox.x + bbox.width / 2;
+            cy = bbox.y + bbox.height / 2;
+        }
+        this.transform(this._.transform.concat([["r", deg, cx, cy]]));
+        return this;
+    };
+    /*\
+     * Element.scale
+     [ method ]
+     **
+     * Deprecated! Use @Element.transform instead.
+     * Adds scale by given amount relative to given point to the list of
+     * transformations of the element.
+     > Parameters
+     - sx (number) horisontal scale amount
+     - sy (number) vertical scale amount
+     - cx (number) #optional x coordinate of the centre of scale
+     - cy (number) #optional y coordinate of the centre of scale
+     * If cx & cy aren’t specified centre of the shape is used instead.
+     = (object) @Element
+    \*/
+    elproto.scale = function (sx, sy, cx, cy) {
+        if (this.removed) {
+            return this;
+        }
+        sx = Str(sx).split(separator);
+        if (sx.length - 1) {
+            sy = toFloat(sx[1]);
+            cx = toFloat(sx[2]);
+            cy = toFloat(sx[3]);
+        }
+        sx = toFloat(sx[0]);
+        (sy == null) && (sy = sx);
+        (cy == null) && (cx = cy);
+        if (cx == null || cy == null) {
+            var bbox = this.getBBox(1);
+        }
+        cx = cx == null ? bbox.x + bbox.width / 2 : cx;
+        cy = cy == null ? bbox.y + bbox.height / 2 : cy;
+        this.transform(this._.transform.concat([["s", sx, sy, cx, cy]]));
+        return this;
+    };
+    /*\
+     * Element.translate
+     [ method ]
+     **
+     * Deprecated! Use @Element.transform instead.
+     * Adds translation by given amount to the list of transformations of the element.
+     > Parameters
+     - dx (number) horisontal shift
+     - dy (number) vertical shift
+     = (object) @Element
+    \*/
+    elproto.translate = function (dx, dy) {
+        if (this.removed) {
+            return this;
+        }
+        dx = Str(dx).split(separator);
+        if (dx.length - 1) {
+            dy = toFloat(dx[1]);
+        }
+        dx = toFloat(dx[0]) || 0;
+        dy = +dy || 0;
+        this.transform(this._.transform.concat([["t", dx, dy]]));
+        return this;
+    };
+    /*\
+     * Element.transform
+     [ method ]
+     **
+     * Adds transformation to the element which is separate to other attributes,
+     * i.e. translation doesn’t change `x` or `y` of the rectange. The format
+     * of transformation string is similar to the path string syntax:
+     | "t100,100r30,100,100s2,2,100,100r45s1.5"
+     * Each letter is a command. There are four commands: `t` is for translate, `r` is for rotate, `s` is for
+     * scale and `m` is for matrix.
+     *
+     * There are also alternative “absolute” translation, rotation and scale: `T`, `R` and `S`. They will not take previous transformation into account. For example, `...T100,0` will always move element 100 px horisontally, while `...t100,0` could move it vertically if there is `r90` before. Just compare results of `r90t100,0` and `r90T100,0`.
+     *
+     * So, the example line above could be read like “translate by 100, 100; rotate 30° around 100, 100; scale twice around 100, 100;
+     * rotate 45° around centre; scale 1.5 times relative to centre”. As you can see rotate and scale commands have origin
+     * coordinates as optional parameters, the default is the centre point of the element.
+     * Matrix accepts six parameters.
+     > Usage
+     | var el = paper.rect(10, 20, 300, 200);
+     | // translate 100, 100, rotate 45°, translate -100, 0
+     | el.transform("t100,100r45t-100,0");
+     | // if you want you can append or prepend transformations
+     | el.transform("...t50,50");
+     | el.transform("s2...");
+     | // or even wrap
+     | el.transform("t50,50...t-50-50");
+     | // to reset transformation call method with empty string
+     | el.transform("");
+     | // to get current value call it without parameters
+     | console.log(el.transform());
+     > Parameters
+     - tstr (string) #optional transformation string
+     * If tstr isn’t specified
+     = (string) current transformation string
+     * else
+     = (object) @Element
+    \*/
+    elproto.transform = function (tstr) {
+        var _ = this._;
+        if (tstr == null) {
+            return _.transform;
+        }
+        R._extractTransform(this, tstr);
+
+        this.clip && $(this.clip, {transform: this.matrix.invert()});
+        this.pattern && updatePosition(this);
+        this.node && $(this.node, {transform: this.matrix});
+
+        if (_.sx != 1 || _.sy != 1) {
+            var sw = this.attrs[has]("stroke-width") ? this.attrs["stroke-width"] : 1;
+            this.attr({"stroke-width": sw});
+        }
+
+        return this;
+    };
+    /*\
+     * Element.hide
+     [ method ]
+     **
+     * Makes element invisible. See @Element.show.
+     = (object) @Element
+    \*/
+    elproto.hide = function () {
+        !this.removed && this.paper.safari(this.node.style.display = "none");
+        return this;
+    };
+    /*\
+     * Element.show
+     [ method ]
+     **
+     * Makes element visible. See @Element.hide.
+     = (object) @Element
+    \*/
+    elproto.show = function () {
+        !this.removed && this.paper.safari(this.node.style.display = "");
+        return this;
+    };
+    /*\
+     * Element.remove
+     [ method ]
+     **
+     * Removes element from the paper.
+    \*/
+    elproto.remove = function () {
+        var node = getRealNode(this.node);
+        if (this.removed || !node.parentNode) {
+            return;
+        }
+        var paper = this.paper;
+        paper.__set__ && paper.__set__.exclude(this);
+        eve.unbind("raphael.*.*." + this.id);
+        if (this.gradient) {
+            paper.defs.removeChild(this.gradient);
+        }
+        R._tear(this, paper);
+
+        node.parentNode.removeChild(node);
+
+        // Remove custom data for element
+        this.removeData();
+
+        for (var i in this) {
+            this[i] = typeof this[i] == "function" ? R._removedFactory(i) : null;
+        }
+        this.removed = true;
+    };
+    elproto._getBBox = function () {
+        if (this.node.style.display == "none") {
+            this.show();
+            var hide = true;
+        }
+        var canvasHidden = false,
+            containerStyle;
+        if (this.paper.canvas.parentElement) {
+          containerStyle = this.paper.canvas.parentElement.style;
+        } //IE10+ can't find parentElement
+        else if (this.paper.canvas.parentNode) {
+          containerStyle = this.paper.canvas.parentNode.style;
+        }
+
+        if(containerStyle && containerStyle.display == "none") {
+          canvasHidden = true;
+          containerStyle.display = "";
+        }
+        var bbox = {};
+        try {
+            bbox = this.node.getBBox();
+        } catch(e) {
+            // Firefox 3.0.x, 25.0.1 (probably more versions affected) play badly here - possible fix
+            bbox = {
+                x: this.node.clientLeft,
+                y: this.node.clientTop,
+                width: this.node.clientWidth,
+                height: this.node.clientHeight
+            }
+        } finally {
+            bbox = bbox || {};
+            if(canvasHidden){
+              containerStyle.display = "none";
+            }
+        }
+        hide && this.hide();
+        return bbox;
+    };
+    /*\
+     * Element.attr
+     [ method ]
+     **
+     * Sets the attributes of the element.
+     > Parameters
+     - attrName (string) attribute’s name
+     - value (string) value
+     * or
+     - params (object) object of name/value pairs
+     * or
+     - attrName (string) attribute’s name
+     * or
+     - attrNames (array) in this case method returns array of current values for given attribute names
+     = (object) @Element if attrsName & value or params are passed in.
+     = (...) value of the attribute if only attrsName is passed in.
+     = (array) array of values of the attribute if attrsNames is passed in.
+     = (object) object of attributes if nothing is passed in.
+     > Possible parameters
+     # <p>Please refer to the <a href="http://www.w3.org/TR/SVG/" title="The W3C Recommendation for the SVG language describes these properties in detail.">SVG specification</a> for an explanation of these parameters.</p>
+     o arrow-end (string) arrowhead on the end of the path. The format for string is `<type>[-<width>[-<length>]]`. Possible types: `classic`, `block`, `open`, `oval`, `diamond`, `none`, width: `wide`, `narrow`, `medium`, length: `long`, `short`, `midium`.
+     o clip-rect (string) comma or space separated values: x, y, width and height
+     o cursor (string) CSS type of the cursor
+     o cx (number) the x-axis coordinate of the center of the circle, or ellipse
+     o cy (number) the y-axis coordinate of the center of the circle, or ellipse
+     o fill (string) colour, gradient or image
+     o fill-opacity (number)
+     o font (string)
+     o font-family (string)
+     o font-size (number) font size in pixels
+     o font-weight (string)
+     o height (number)
+     o href (string) URL, if specified element behaves as hyperlink
+     o opacity (number)
+     o path (string) SVG path string format
+     o r (number) radius of the circle, ellipse or rounded corner on the rect
+     o rx (number) horisontal radius of the ellipse
+     o ry (number) vertical radius of the ellipse
+     o src (string) image URL, only works for @Element.image element
+     o stroke (string) stroke colour
+     o stroke-dasharray (string) [“”, “`-`”, “`.`”, “`-.`”, “`-..`”, “`. `”, “`- `”, “`--`”, “`- .`”, “`--.`”, “`--..`”]
+     o stroke-linecap (string) [“`butt`”, “`square`”, “`round`”]
+     o stroke-linejoin (string) [“`bevel`”, “`round`”, “`miter`”]
+     o stroke-miterlimit (number)
+     o stroke-opacity (number)
+     o stroke-width (number) stroke width in pixels, default is '1'
+     o target (string) used with href
+     o text (string) contents of the text element. Use `\n` for multiline text
+     o text-anchor (string) [“`start`”, “`middle`”, “`end`”], default is “`middle`”
+     o title (string) will create tooltip with a given text
+     o transform (string) see @Element.transform
+     o width (number)
+     o x (number)
+     o y (number)
+     > Gradients
+     * Linear gradient format: “`‹angle›-‹colour›[-‹colour›[:‹offset›]]*-‹colour›`”, example: “`90-#fff-#000`” – 90°
+     * gradient from white to black or “`0-#fff-#f00:20-#000`” – 0° gradient from white via red (at 20%) to black.
+     *
+     * radial gradient: “`r[(‹fx›, ‹fy›)]‹colour›[-‹colour›[:‹offset›]]*-‹colour›`”, example: “`r#fff-#000`” –
+     * gradient from white to black or “`r(0.25, 0.75)#fff-#000`” – gradient from white to black with focus point
+     * at 0.25, 0.75. Focus point coordinates are in 0..1 range. Radial gradients can only be applied to circles and ellipses.
+     > Path String
+     # <p>Please refer to <a href="http://www.w3.org/TR/SVG/paths.html#PathData" title="Details of a path’s data attribute’s format are described in the SVG specification.">SVG documentation regarding path string</a>. Raphaël fully supports it.</p>
+     > Colour Parsing
+     # <ul>
+     #     <li>Colour name (“<code>red</code>”, “<code>green</code>”, “<code>cornflowerblue</code>”, etc)</li>
+     #     <li>#••• — shortened HTML colour: (“<code>#000</code>”, “<code>#fc0</code>”, etc)</li>
+     #     <li>#•••••• — full length HTML colour: (“<code>#000000</code>”, “<code>#bd2300</code>”)</li>
+     #     <li>rgb(•••, •••, •••) — red, green and blue channels’ values: (“<code>rgb(200,&nbsp;100,&nbsp;0)</code>”)</li>
+     #     <li>rgb(•••%, •••%, •••%) — same as above, but in %: (“<code>rgb(100%,&nbsp;175%,&nbsp;0%)</code>”)</li>
+     #     <li>rgba(•••, •••, •••, •••) — red, green and blue channels’ values: (“<code>rgba(200,&nbsp;100,&nbsp;0, .5)</code>”)</li>
+     #     <li>rgba(•••%, •••%, •••%, •••%) — same as above, but in %: (“<code>rgba(100%,&nbsp;175%,&nbsp;0%, 50%)</code>”)</li>
+     #     <li>hsb(•••, •••, •••) — hue, saturation and brightness values: (“<code>hsb(0.5,&nbsp;0.25,&nbsp;1)</code>”)</li>
+     #     <li>hsb(•••%, •••%, •••%) — same as above, but in %</li>
+     #     <li>hsba(•••, •••, •••, •••) — same as above, but with opacity</li>
+     #     <li>hsl(•••, •••, •••) — almost the same as hsb, see <a href="http://en.wikipedia.org/wiki/HSL_and_HSV" title="HSL and HSV - Wikipedia, the free encyclopedia">Wikipedia page</a></li>
+     #     <li>hsl(•••%, •••%, •••%) — same as above, but in %</li>
+     #     <li>hsla(•••, •••, •••, •••) — same as above, but with opacity</li>
+     #     <li>Optionally for hsb and hsl you could specify hue as a degree: “<code>hsl(240deg,&nbsp;1,&nbsp;.5)</code>” or, if you want to go fancy, “<code>hsl(240°,&nbsp;1,&nbsp;.5)</code>”</li>
+     # </ul>
+    \*/
+    elproto.attr = function (name, value) {
+        if (this.removed) {
+            return this;
+        }
+        if (name == null) {
+            var res = {};
+            for (var a in this.attrs) if (this.attrs[has](a)) {
+                res[a] = this.attrs[a];
+            }
+            res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
+            res.transform = this._.transform;
+            return res;
+        }
+        if (value == null && R.is(name, "string")) {
+            if (name == "fill" && this.attrs.fill == "none" && this.attrs.gradient) {
+                return this.attrs.gradient;
+            }
+            if (name == "transform") {
+                return this._.transform;
+            }
+            var names = name.split(separator),
+                out = {};
+            for (var i = 0, ii = names.length; i < ii; i++) {
+                name = names[i];
+                if (name in this.attrs) {
+                    out[name] = this.attrs[name];
+                } else if (R.is(this.paper.customAttributes[name], "function")) {
+                    out[name] = this.paper.customAttributes[name].def;
+                } else {
+                    out[name] = R._availableAttrs[name];
+                }
+            }
+            return ii - 1 ? out : out[names[0]];
+        }
+        if (value == null && R.is(name, "array")) {
+            out = {};
+            for (i = 0, ii = name.length; i < ii; i++) {
+                out[name[i]] = this.attr(name[i]);
+            }
+            return out;
+        }
+        if (value != null) {
+            var params = {};
+            params[name] = value;
+        } else if (name != null && R.is(name, "object")) {
+            params = name;
+        }
+        for (var key in params) {
+            eve("raphael.attr." + key + "." + this.id, this, params[key]);
+        }
+        for (key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) {
+            var par = this.paper.customAttributes[key].apply(this, [].concat(params[key]));
+            this.attrs[key] = params[key];
+            for (var subkey in par) if (par[has](subkey)) {
+                params[subkey] = par[subkey];
+            }
+        }
+        setFillAndStroke(this, params);
+        return this;
+    };
+    /*\
+     * Element.toFront
+     [ method ]
+     **
+     * Moves the element so it is the closest to the viewer’s eyes, on top of other elements.
+     = (object) @Element
+    \*/
+    elproto.toFront = function () {
+        if (this.removed) {
+            return this;
+        }
+        var node = getRealNode(this.node);
+        node.parentNode.appendChild(node);
+        var svg = this.paper;
+        svg.top != this && R._tofront(this, svg);
+        return this;
+    };
+    /*\
+     * Element.toBack
+     [ method ]
+     **
+     * Moves the element so it is the furthest from the viewer’s eyes, behind other elements.
+     = (object) @Element
+    \*/
+    elproto.toBack = function () {
+        if (this.removed) {
+            return this;
+        }
+        var node = getRealNode(this.node);
+        var parentNode = node.parentNode;
+        parentNode.insertBefore(node, parentNode.firstChild);
+        R._toback(this, this.paper);
+        var svg = this.paper;
+        return this;
+    };
+    /*\
+     * Element.insertAfter
+     [ method ]
+     **
+     * Inserts current object after the given one.
+     = (object) @Element
+    \*/
+    elproto.insertAfter = function (element) {
+        if (this.removed || !element) {
+            return this;
+        }
+
+        var node = getRealNode(this.node);
+        var afterNode = getRealNode(element.node || element[element.length - 1].node);
+        if (afterNode.nextSibling) {
+            afterNode.parentNode.insertBefore(node, afterNode.nextSibling);
+        } else {
+            afterNode.parentNode.appendChild(node);
+        }
+        R._insertafter(this, element, this.paper);
+        return this;
+    };
+    /*\
+     * Element.insertBefore
+     [ method ]
+     **
+     * Inserts current object before the given one.
+     = (object) @Element
+    \*/
+    elproto.insertBefore = function (element) {
+        if (this.removed || !element) {
+            return this;
+        }
+
+        var node = getRealNode(this.node);
+        var beforeNode = getRealNode(element.node || element[0].node);
+        beforeNode.parentNode.insertBefore(node, beforeNode);
+        R._insertbefore(this, element, this.paper);
+        return this;
+    };
+    elproto.blur = function (size) {
+        // Experimental. No Safari support. Use it on your own risk.
+        var t = this;
+        if (+size !== 0) {
+            var fltr = $("filter"),
+                blur = $("feGaussianBlur");
+            t.attrs.blur = size;
+            fltr.id = R.createUUID();
+            $(blur, {stdDeviation: +size || 1.5});
+            fltr.appendChild(blur);
+            t.paper.defs.appendChild(fltr);
+            t._blur = fltr;
+            $(t.node, {filter: "url(#" + fltr.id + ")"});
+        } else {
+            if (t._blur) {
+                t._blur.parentNode.removeChild(t._blur);
+                delete t._blur;
+                delete t.attrs.blur;
+            }
+            t.node.removeAttribute("filter");
+        }
+        return t;
+    };
+    R._engine.circle = function (svg, x, y, r) {
+        var el = $("circle");
+        svg.canvas && svg.canvas.appendChild(el);
+        var res = new Element(el, svg);
+        res.attrs = {cx: x, cy: y, r: r, fill: "none", stroke: "#000"};
+        res.type = "circle";
+        $(el, res.attrs);
+        return res;
+    };
+    R._engine.rect = function (svg, x, y, w, h, r) {
+        var el = $("rect");
+        svg.canvas && svg.canvas.appendChild(el);
+        var res = new Element(el, svg);
+        res.attrs = {x: x, y: y, width: w, height: h, rx: r || 0, ry: r || 0, fill: "none", stroke: "#000"};
+        res.type = "rect";
+        $(el, res.attrs);
+        return res;
+    };
+    R._engine.ellipse = function (svg, x, y, rx, ry) {
+        var el = $("ellipse");
+        svg.canvas && svg.canvas.appendChild(el);
+        var res = new Element(el, svg);
+        res.attrs = {cx: x, cy: y, rx: rx, ry: ry, fill: "none", stroke: "#000"};
+        res.type = "ellipse";
+        $(el, res.attrs);
+        return res;
+    };
+    R._engine.image = function (svg, src, x, y, w, h) {
+        var el = $("image");
+        $(el, {x: x, y: y, width: w, height: h, preserveAspectRatio: "none"});
+        el.setAttributeNS(xlink, "href", src);
+        svg.canvas && svg.canvas.appendChild(el);
+        var res = new Element(el, svg);
+        res.attrs = {x: x, y: y, width: w, height: h, src: src};
+        res.type = "image";
+        return res;
+    };
+    R._engine.text = function (svg, x, y, text) {
+        var el = $("text");
+        svg.canvas && svg.canvas.appendChild(el);
+        var res = new Element(el, svg);
+        res.attrs = {
+            x: x,
+            y: y,
+            "text-anchor": "middle",
+            text: text,
+            "font-family": R._availableAttrs["font-family"],
+            "font-size": R._availableAttrs["font-size"],
+            stroke: "none",
+            fill: "#000"
+        };
+        res.type = "text";
+        setFillAndStroke(res, res.attrs);
+        return res;
+    };
+    R._engine.setSize = function (width, height) {
+        this.width = width || this.width;
+        this.height = height || this.height;
+        this.canvas.setAttribute("width", this.width);
+        this.canvas.setAttribute("height", this.height);
+        if (this._viewBox) {
+            this.setViewBox.apply(this, this._viewBox);
+        }
+        return this;
+    };
+    R._engine.create = function () {
+        var con = R._getContainer.apply(0, arguments),
+            container = con && con.container,
+            x = con.x,
+            y = con.y,
+            width = con.width,
+            height = con.height;
+        if (!container) {
+            throw new Error("SVG container not found.");
+        }
+        var cnvs = $("svg"),
+            css = "overflow:hidden;",
+            isFloating;
+        x = x || 0;
+        y = y || 0;
+        width = width || 512;
+        height = height || 342;
+        $(cnvs, {
+            height: height,
+            version: 1.1,
+            width: width,
+            xmlns: "http://www.w3.org/2000/svg",
+            "xmlns:xlink": "http://www.w3.org/1999/xlink"
+        });
+        if (container == 1) {
+            cnvs.style.cssText = css + "position:absolute;left:" + x + "px;top:" + y + "px";
+            R._g.doc.body.appendChild(cnvs);
+            isFloating = 1;
+        } else {
+            cnvs.style.cssText = css + "position:relative";
+            if (container.firstChild) {
+                container.insertBefore(cnvs, container.firstChild);
+            } else {
+                container.appendChild(cnvs);
+            }
+        }
+        container = new R._Paper;
+        container.width = width;
+        container.height = height;
+        container.canvas = cnvs;
+        container.clear();
+        container._left = container._top = 0;
+        isFloating && (container.renderfix = function () {});
+        container.renderfix();
+        return container;
+    };
+    R._engine.setViewBox = function (x, y, w, h, fit) {
+        eve("raphael.setViewBox", this, this._viewBox, [x, y, w, h, fit]);
+        var paperSize = this.getSize(),
+            size = mmax(w / paperSize.width, h / paperSize.height),
+            top = this.top,
+            aspectRatio = fit ? "xMidYMid meet" : "xMinYMin",
+            vb,
+            sw;
+        if (x == null) {
+            if (this._vbSize) {
+                size = 1;
+            }
+            delete this._vbSize;
+            vb = "0 0 " + this.width + S + this.height;
+        } else {
+            this._vbSize = size;
+            vb = x + S + y + S + w + S + h;
+        }
+        $(this.canvas, {
+            viewBox: vb,
+            preserveAspectRatio: aspectRatio
+        });
+        while (size && top) {
+            sw = "stroke-width" in top.attrs ? top.attrs["stroke-width"] : 1;
+            top.attr({"stroke-width": sw});
+            top._.dirty = 1;
+            top._.dirtyT = 1;
+            top = top.prev;
+        }
+        this._viewBox = [x, y, w, h, !!fit];
+        return this;
+    };
+    /*\
+     * Paper.renderfix
+     [ method ]
+     **
+     * Fixes the issue of Firefox and IE9 regarding subpixel rendering. If paper is dependant
+     * on other elements after reflow it could shift half pixel which cause for lines to lost their crispness.
+     * This method fixes the issue.
+     **
+       Special thanks to Mariusz Nowak (http://www.medikoo.com/) for this method.
+    \*/
+    R.prototype.renderfix = function () {
+        var cnvs = this.canvas,
+            s = cnvs.style,
+            pos;
+        try {
+            pos = cnvs.getScreenCTM() || cnvs.createSVGMatrix();
+        } catch (e) {
+            pos = cnvs.createSVGMatrix();
+        }
+        var left = -pos.e % 1,
+            top = -pos.f % 1;
+        if (left || top) {
+            if (left) {
+                this._left = (this._left + left) % 1;
+                s.left = this._left + "px";
+            }
+            if (top) {
+                this._top = (this._top + top) % 1;
+                s.top = this._top + "px";
+            }
+        }
+    };
+    /*\
+     * Paper.clear
+     [ method ]
+     **
+     * Clears the paper, i.e. removes all the elements.
+    \*/
+    R.prototype.clear = function () {
+        R.eve("raphael.clear", this);
+        var c = this.canvas;
+        while (c.firstChild) {
+            c.removeChild(c.firstChild);
+        }
+        this.bottom = this.top = null;
+        (this.desc = $("desc")).appendChild(R._g.doc.createTextNode("Created with Rapha\xebl " + R.version));
+        c.appendChild(this.desc);
+        c.appendChild(this.defs = $("defs"));
+    };
+    /*\
+     * Paper.remove
+     [ method ]
+     **
+     * Removes the paper from the DOM.
+    \*/
+    R.prototype.remove = function () {
+        eve("raphael.remove", this);
+        this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas);
+        for (var i in this) {
+            this[i] = typeof this[i] == "function" ? R._removedFactory(i) : null;
+        }
+    };
+    var setproto = R.st;
+    for (var method in elproto) if (elproto[has](method) && !setproto[has](method)) {
+        setproto[method] = (function (methodname) {
+            return function () {
+                var arg = arguments;
+                return this.forEach(function (el) {
+                    el[methodname].apply(el, arg);
+                });
+            };
+        })(method);
+    }
+})();
+
+// ┌─────────────────────────────────────────────────────────────────────┐ \\
+// │ Raphaël - JavaScript Vector Library                                 │ \\
+// ├─────────────────────────────────────────────────────────────────────┤ \\
+// │ VML Module                                                          │ \\
+// ├─────────────────────────────────────────────────────────────────────┤ \\
+// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com)   │ \\
+// │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com)             │ \\
+// │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
+// └─────────────────────────────────────────────────────────────────────┘ \\
+
+(function(){
+    if (!R.vml) {
+        return;
+    }
+    var has = "hasOwnProperty",
+        Str = String,
+        toFloat = parseFloat,
+        math = Math,
+        round = math.round,
+        mmax = math.max,
+        mmin = math.min,
+        abs = math.abs,
+        fillString = "fill",
+        separator = /[, ]+/,
+        eve = R.eve,
+        ms = " progid:DXImageTransform.Microsoft",
+        S = " ",
+        E = "",
+        map = {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"},
+        bites = /([clmz]),?([^clmz]*)/gi,
+        blurregexp = / progid:\S+Blur\([^\)]+\)/g,
+        val = /-?[^,\s-]+/g,
+        cssDot = "position:absolute;left:0;top:0;width:1px;height:1px;behavior:url(#default#VML)",
+        zoom = 21600,
+        pathTypes = {path: 1, rect: 1, image: 1},
+        ovalTypes = {circle: 1, ellipse: 1},
+        path2vml = function (path) {
+            var total =  /[ahqstv]/ig,
+                command = R._pathToAbsolute;
+            Str(path).match(total) && (command = R._path2curve);
+            total = /[clmz]/g;
+            if (command == R._pathToAbsolute && !Str(path).match(total)) {
+                var res = Str(path).replace(bites, function (all, command, args) {
+                    var vals = [],
+                        isMove = command.toLowerCase() == "m",
+                        res = map[command];
+                    args.replace(val, function (value) {
+                        if (isMove && vals.length == 2) {
+                            res += vals + map[command == "m" ? "l" : "L"];
+                            vals = [];
+                        }
+                        vals.push(round(value * zoom));
+                    });
+                    return res + vals;
+                });
+                return res;
+            }
+            var pa = command(path), p, r;
+            res = [];
+            for (var i = 0, ii = pa.length; i < ii; i++) {
+                p = pa[i];
+                r = pa[i][0].toLowerCase();
+                r == "z" && (r = "x");
+                for (var j = 1, jj = p.length; j < jj; j++) {
+                    r += round(p[j] * zoom) + (j != jj - 1 ? "," : E);
+                }
+                res.push(r);
+            }
+            return res.join(S);
+        },
+        compensation = function (deg, dx, dy) {
+            var m = R.matrix();
+            m.rotate(-deg, .5, .5);
+            return {
+                dx: m.x(dx, dy),
+                dy: m.y(dx, dy)
+            };
+        },
+        setCoords = function (p, sx, sy, dx, dy, deg) {
+            var _ = p._,
+                m = p.matrix,
+                fillpos = _.fillpos,
+                o = p.node,
+                s = o.style,
+                y = 1,
+                flip = "",
+                dxdy,
+                kx = zoom / sx,
+                ky = zoom / sy;
+            s.visibility = "hidden";
+            if (!sx || !sy) {
+                return;
+            }
+            o.coordsize = abs(kx) + S + abs(ky);
+            s.rotation = deg * (sx * sy < 0 ? -1 : 1);
+            if (deg) {
+                var c = compensation(deg, dx, dy);
+                dx = c.dx;
+                dy = c.dy;
+            }
+            sx < 0 && (flip += "x");
+            sy < 0 && (flip += " y") && (y = -1);
+            s.flip = flip;
+            o.coordorigin = (dx * -kx) + S + (dy * -ky);
+            if (fillpos || _.fillsize) {
+                var fill = o.getElementsByTagName(fillString);
+                fill = fill && fill[0];
+                o.removeChild(fill);
+                if (fillpos) {
+                    c = compensation(deg, m.x(fillpos[0], fillpos[1]), m.y(fillpos[0], fillpos[1]));
+                    fill.position = c.dx * y + S + c.dy * y;
+                }
+                if (_.fillsize) {
+                    fill.size = _.fillsize[0] * abs(sx) + S + _.fillsize[1] * abs(sy);
+                }
+                o.appendChild(fill);
+            }
+            s.visibility = "visible";
+        };
+    R.toString = function () {
+        return  "Your browser doesn\u2019t support SVG. Falling down to VML.\nYou are running Rapha\xebl " + this.version;
+    };
+    var addArrow = function (o, value, isEnd) {
+        var values = Str(value).toLowerCase().split("-"),
+            se = isEnd ? "end" : "start",
+            i = values.length,
+            type = "classic",
+            w = "medium",
+            h = "medium";
+        while (i--) {
+            switch (values[i]) {
+                case "block":
+                case "classic":
+                case "oval":
+                case "diamond":
+                case "open":
+                case "none":
+                    type = values[i];
+                    break;
+                case "wide":
+                case "narrow": h = values[i]; break;
+                case "long":
+                case "short": w = values[i]; break;
+            }
+        }
+        var stroke = o.node.getElementsByTagName("stroke")[0];
+        stroke[se + "arrow"] = type;
+        stroke[se + "arrowlength"] = w;
+        stroke[se + "arrowwidth"] = h;
+    },
+    setFillAndStroke = function (o, params) {
+        // o.paper.canvas.style.display = "none";
+        o.attrs = o.attrs || {};
+        var node = o.node,
+            a = o.attrs,
+            s = node.style,
+            xy,
+            newpath = pathTypes[o.type] && (params.x != a.x || params.y != a.y || params.width != a.width || params.height != a.height || params.cx != a.cx || params.cy != a.cy || params.rx != a.rx || params.ry != a.ry || params.r != a.r),
+            isOval = ovalTypes[o.type] && (a.cx != params.cx || a.cy != params.cy || a.r != params.r || a.rx != params.rx || a.ry != params.ry),
+            res = o;
+
+
+        for (var par in params) if (params[has](par)) {
+            a[par] = params[par];
+        }
+        if (newpath) {
+            a.path = R._getPath[o.type](o);
+            o._.dirty = 1;
+        }
+        params.href && (node.href = params.href);
+        params.title && (node.title = params.title);
+        params.target && (node.target = params.target);
+        params.cursor && (s.cursor = params.cursor);
+        "blur" in params && o.blur(params.blur);
+        if (params.path && o.type == "path" || newpath) {
+            node.path = path2vml(~Str(a.path).toLowerCase().indexOf("r") ? R._pathToAbsolute(a.path) : a.path);
+            o._.dirty = 1;
+            if (o.type == "image") {
+                o._.fillpos = [a.x, a.y];
+                o._.fillsize = [a.width, a.height];
+                setCoords(o, 1, 1, 0, 0, 0);
+            }
+        }
+        "transform" in params && o.transform(params.transform);
+        if (isOval) {
+            var cx = +a.cx,
+                cy = +a.cy,
+                rx = +a.rx || +a.r || 0,
+                ry = +a.ry || +a.r || 0;
+            node.path = R.format("ar{0},{1},{2},{3},{4},{1},{4},{1}x", round((cx - rx) * zoom), round((cy - ry) * zoom), round((cx + rx) * zoom), round((cy + ry) * zoom), round(cx * zoom));
+            o._.dirty = 1;
+        }
+        if ("clip-rect" in params) {
+            var rect = Str(params["clip-rect"]).split(separator);
+            if (rect.length == 4) {
+                rect[2] = +rect[2] + (+rect[0]);
+                rect[3] = +rect[3] + (+rect[1]);
+                var div = node.clipRect || R._g.doc.createElement("div"),
+                    dstyle = div.style;
+                dstyle.clip = R.format("rect({1}px {2}px {3}px {0}px)", rect);
+                if (!node.clipRect) {
+                    dstyle.position = "absolute";
+                    dstyle.top = 0;
+                    dstyle.left = 0;
+                    dstyle.width = o.paper.width + "px";
+                    dstyle.height = o.paper.height + "px";
+                    node.parentNode.insertBefore(div, node);
+                    div.appendChild(node);
+                    node.clipRect = div;
+                }
+            }
+            if (!params["clip-rect"]) {
+                node.clipRect && (node.clipRect.style.clip = "auto");
+            }
+        }
+        if (o.textpath) {
+            var textpathStyle = o.textpath.style;
+            params.font && (textpathStyle.font = params.font);
+            params["font-family"] && (textpathStyle.fontFamily = '"' + params["font-family"].split(",")[0].replace(/^['"]+|['"]+$/g, E) + '"');
+            params["font-size"] && (textpathStyle.fontSize = params["font-size"]);
+            params["font-weight"] && (textpathStyle.fontWeight = params["font-weight"]);
+            params["font-style"] && (textpathStyle.fontStyle = params["font-style"]);
+        }
+        if ("arrow-start" in params) {
+            addArrow(res, params["arrow-start"]);
+        }
+        if ("arrow-end" in params) {
+            addArrow(res, params["arrow-end"], 1);
+        }
+        if (params.opacity != null || 
+            params["stroke-width"] != null ||
+            params.fill != null ||
+            params.src != null ||
+            params.stroke != null ||
+            params["stroke-width"] != null ||
+            params["stroke-opacity"] != null ||
+            params["fill-opacity"] != null ||
+            params["stroke-dasharray"] != null ||
+            params["stroke-miterlimit"] != null ||
+            params["stroke-linejoin"] != null ||
+            params["stroke-linecap"] != null) {
+            var fill = node.getElementsByTagName(fillString),
+                newfill = false;
+            fill = fill && fill[0];
+            !fill && (newfill = fill = createNode(fillString));
+            if (o.type == "image" && params.src) {
+                fill.src = params.src;
+            }
+            params.fill && (fill.on = true);
+            if (fill.on == null || params.fill == "none" || params.fill === null) {
+                fill.on = false;
+            }
+            if (fill.on && params.fill) {
+                var isURL = Str(params.fill).match(R._ISURL);
+                if (isURL) {
+                    fill.parentNode == node && node.removeChild(fill);
+                    fill.rotate = true;
+                    fill.src = isURL[1];
+                    fill.type = "tile";
+                    var bbox = o.getBBox(1);
+                    fill.position = bbox.x + S + bbox.y;
+                    o._.fillpos = [bbox.x, bbox.y];
+
+                    R._preload(isURL[1], function () {
+                        o._.fillsize = [this.offsetWidth, this.offsetHeight];
+                    });
+                } else {
+                    fill.color = R.getRGB(params.fill).hex;
+                    fill.src = E;
+                    fill.type = "solid";
+                    if (R.getRGB(params.fill).error && (res.type in {circle: 1, ellipse: 1} || Str(params.fill).charAt() != "r") && addGradientFill(res, params.fill, fill)) {
+                        a.fill = "none";
+                        a.gradient = params.fill;
+                        fill.rotate = false;
+                    }
+                }
+            }
+            if ("fill-opacity" in params || "opacity" in params) {
+                var opacity = ((+a["fill-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+R.getRGB(params.fill).o + 1 || 2) - 1);
+                opacity = mmin(mmax(opacity, 0), 1);
+                fill.opacity = opacity;
+                if (fill.src) {
+                    fill.color = "none";
+                }
+            }
+            node.appendChild(fill);
+            var stroke = (node.getElementsByTagName("stroke") && node.getElementsByTagName("stroke")[0]),
+            newstroke = false;
+            !stroke && (newstroke = stroke = createNode("stroke"));
+            if ((params.stroke && params.stroke != "none") ||
+                params["stroke-width"] ||
+                params["stroke-opacity"] != null ||
+                params["stroke-dasharray"] ||
+                params["stroke-miterlimit"] ||
+                params["stroke-linejoin"] ||
+                params["stroke-linecap"]) {
+                stroke.on = true;
+            }
+            (params.stroke == "none" || params.stroke === null || stroke.on == null || params.stroke == 0 || params["stroke-width"] == 0) && (stroke.on = false);
+            var strokeColor = R.getRGB(params.stroke);
+            stroke.on && params.stroke && (stroke.color = strokeColor.hex);
+            opacity = ((+a["stroke-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+strokeColor.o + 1 || 2) - 1);
+            var width = (toFloat(params["stroke-width"]) || 1) * .75;
+            opacity = mmin(mmax(opacity, 0), 1);
+            params["stroke-width"] == null && (width = a["stroke-width"]);
+            params["stroke-width"] && (stroke.weight = width);
+            width && width < 1 && (opacity *= width) && (stroke.weight = 1);
+            stroke.opacity = opacity;
+        
+            params["stroke-linejoin"] && (stroke.joinstyle = params["stroke-linejoin"] || "miter");
+            stroke.miterlimit = params["stroke-miterlimit"] || 8;
+            params["stroke-linecap"] && (stroke.endcap = params["stroke-linecap"] == "butt" ? "flat" : params["stroke-linecap"] == "square" ? "square" : "round");
+            if ("stroke-dasharray" in params) {
+                var dasharray = {
+                    "-": "shortdash",
+                    ".": "shortdot",
+                    "-.": "shortdashdot",
+                    "-..": "shortdashdotdot",
+                    ". ": "dot",
+                    "- ": "dash",
+                    "--": "longdash",
+                    "- .": "dashdot",
+                    "--.": "longdashdot",
+                    "--..": "longdashdotdot"
+                };
+                stroke.dashstyle = dasharray[has](params["stroke-dasharray"]) ? dasharray[params["stroke-dasharray"]] : E;
+            }
+            newstroke && node.appendChild(stroke);
+        }
+        if (res.type == "text") {
+            res.paper.canvas.style.display = E;
+            var span = res.paper.span,
+                m = 100,
+                fontSize = a.font && a.font.match(/\d+(?:\.\d*)?(?=px)/);
+            s = span.style;
+            a.font && (s.font = a.font);
+            a["font-family"] && (s.fontFamily = a["font-family"]);
+            a["font-weight"] && (s.fontWeight = a["font-weight"]);
+            a["font-style"] && (s.fontStyle = a["font-style"]);
+            fontSize = toFloat(a["font-size"] || fontSize && fontSize[0]) || 10;
+            s.fontSize = fontSize * m + "px";
+            res.textpath.string && (span.innerHTML = Str(res.textpath.string).replace(/</g, "&#60;").replace(/&/g, "&#38;").replace(/\n/g, "<br>"));
+            var brect = span.getBoundingClientRect();
+            res.W = a.w = (brect.right - brect.left) / m;
+            res.H = a.h = (brect.bottom - brect.top) / m;
+            // res.paper.canvas.style.display = "none";
+            res.X = a.x;
+            res.Y = a.y + res.H / 2;
+
+            ("x" in params || "y" in params) && (res.path.v = R.format("m{0},{1}l{2},{1}", round(a.x * zoom), round(a.y * zoom), round(a.x * zoom) + 1));
+            var dirtyattrs = ["x", "y", "text", "font", "font-family", "font-weight", "font-style", "font-size"];
+            for (var d = 0, dd = dirtyattrs.length; d < dd; d++) if (dirtyattrs[d] in params) {
+                res._.dirty = 1;
+                break;
+            }
+        
+            // text-anchor emulation
+            switch (a["text-anchor"]) {
+                case "start":
+                    res.textpath.style["v-text-align"] = "left";
+                    res.bbx = res.W / 2;
+                break;
+                case "end":
+                    res.textpath.style["v-text-align"] = "right";
+                    res.bbx = -res.W / 2;
+                break;
+                default:
+                    res.textpath.style["v-text-align"] = "center";
+                    res.bbx = 0;
+                break;
+            }
+            res.textpath.style["v-text-kern"] = true;
+        }
+        // res.paper.canvas.style.display = E;
+    },
+    addGradientFill = function (o, gradient, fill) {
+        o.attrs = o.attrs || {};
+        var attrs = o.attrs,
+            pow = Math.pow,
+            opacity,
+            oindex,
+            type = "linear",
+            fxfy = ".5 .5";
+        o.attrs.gradient = gradient;
+        gradient = Str(gradient).replace(R._radial_gradient, function (all, fx, fy) {
+            type = "radial";
+            if (fx && fy) {
+                fx = toFloat(fx);
+                fy = toFloat(fy);
+                pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && (fy = math.sqrt(.25 - pow(fx - .5, 2)) * ((fy > .5) * 2 - 1) + .5);
+                fxfy = fx + S + fy;
+            }
+            return E;
+        });
+        gradient = gradient.split(/\s*\-\s*/);
+        if (type == "linear") {
+            var angle = gradient.shift();
+            angle = -toFloat(angle);
+            if (isNaN(angle)) {
+                return null;
+            }
+        }
+        var dots = R._parseDots(gradient);
+        if (!dots) {
+            return null;
+        }
+        o = o.shape || o.node;
+        if (dots.length) {
+            o.removeChild(fill);
+            fill.on = true;
+            fill.method = "none";
+            fill.color = dots[0].color;
+            fill.color2 = dots[dots.length - 1].color;
+            var clrs = [];
+            for (var i = 0, ii = dots.length; i < ii; i++) {
+                dots[i].offset && clrs.push(dots[i].offset + S + dots[i].color);
+            }
+            fill.colors = clrs.length ? clrs.join() : "0% " + fill.color;
+            if (type == "radial") {
+                fill.type = "gradientTitle";
+                fill.focus = "100%";
+                fill.focussize = "0 0";
+                fill.focusposition = fxfy;
+                fill.angle = 0;
+            } else {
+                // fill.rotate= true;
+                fill.type = "gradient";
+                fill.angle = (270 - angle) % 360;
+            }
+            o.appendChild(fill);
+        }
+        return 1;
+    },
+    Element = function (node, vml) {
+        this[0] = this.node = node;
+        node.raphael = true;
+        this.id = R._oid++;
+        node.raphaelid = this.id;
+        this.X = 0;
+        this.Y = 0;
+        this.attrs = {};
+        this.paper = vml;
+        this.matrix = R.matrix();
+        this._ = {
+            transform: [],
+            sx: 1,
+            sy: 1,
+            dx: 0,
+            dy: 0,
+            deg: 0,
+            dirty: 1,
+            dirtyT: 1
+        };
+        !vml.bottom && (vml.bottom = this);
+        this.prev = vml.top;
+        vml.top && (vml.top.next = this);
+        vml.top = this;
+        this.next = null;
+    };
+    var elproto = R.el;
+
+    Element.prototype = elproto;
+    elproto.constructor = Element;
+    elproto.transform = function (tstr) {
+        if (tstr == null) {
+            return this._.transform;
+        }
+        var vbs = this.paper._viewBoxShift,
+            vbt = vbs ? "s" + [vbs.scale, vbs.scale] + "-1-1t" + [vbs.dx, vbs.dy] : E,
+            oldt;
+        if (vbs) {
+            oldt = tstr = Str(tstr).replace(/\.{3}|\u2026/g, this._.transform || E);
+        }
+        R._extractTransform(this, vbt + tstr);
+        var matrix = this.matrix.clone(),
+            skew = this.skew,
+            o = this.node,
+            split,
+            isGrad = ~Str(this.attrs.fill).indexOf("-"),
+            isPatt = !Str(this.attrs.fill).indexOf("url(");
+        matrix.translate(1, 1);
+        if (isPatt || isGrad || this.type == "image") {
+            skew.matrix = "1 0 0 1";
+            skew.offset = "0 0";
+            split = matrix.split();
+            if ((isGrad && split.noRotation) || !split.isSimple) {
+                o.style.filter = matrix.toFilter();
+                var bb = this.getBBox(),
+                    bbt = this.getBBox(1),
+                    dx = bb.x - bbt.x,
+                    dy = bb.y - bbt.y;
+                o.coordorigin = (dx * -zoom) + S + (dy * -zoom);
+                setCoords(this, 1, 1, dx, dy, 0);
+            } else {
+                o.style.filter = E;
+                setCoords(this, split.scalex, split.scaley, split.dx, split.dy, split.rotate);
+            }
+        } else {
+            o.style.filter = E;
+            skew.matrix = Str(matrix);
+            skew.offset = matrix.offset();
+        }
+        if (oldt !== null) { // empty string value is true as well
+            this._.transform = oldt;
+            R._extractTransform(this, oldt);
+        }
+        return this;
+    };
+    elproto.rotate = function (deg, cx, cy) {
+        if (this.removed) {
+            return this;
+        }
+        if (deg == null) {
+            return;
+        }
+        deg = Str(deg).split(separator);
+        if (deg.length - 1) {
+            cx = toFloat(deg[1]);
+            cy = toFloat(deg[2]);
+        }
+        deg = toFloat(deg[0]);
+        (cy == null) && (cx = cy);
+        if (cx == null || cy == null) {
+            var bbox = this.getBBox(1);
+            cx = bbox.x + bbox.width / 2;
+            cy = bbox.y + bbox.height / 2;
+        }
+        this._.dirtyT = 1;
+        this.transform(this._.transform.concat([["r", deg, cx, cy]]));
+        return this;
+    };
+    elproto.translate = function (dx, dy) {
+        if (this.removed) {
+            return this;
+        }
+        dx = Str(dx).split(separator);
+        if (dx.length - 1) {
+            dy = toFloat(dx[1]);
+        }
+        dx = toFloat(dx[0]) || 0;
+        dy = +dy || 0;
+        if (this._.bbox) {
+            this._.bbox.x += dx;
+            this._.bbox.y += dy;
+        }
+        this.transform(this._.transform.concat([["t", dx, dy]]));
+        return this;
+    };
+    elproto.scale = function (sx, sy, cx, cy) {
+        if (this.removed) {
+            return this;
+        }
+        sx = Str(sx).split(separator);
+        if (sx.length - 1) {
+            sy = toFloat(sx[1]);
+            cx = toFloat(sx[2]);
+            cy = toFloat(sx[3]);
+            isNaN(cx) && (cx = null);
+            isNaN(cy) && (cy = null);
+        }
+        sx = toFloat(sx[0]);
+        (sy == null) && (sy = sx);
+        (cy == null) && (cx = cy);
+        if (cx == null || cy == null) {
+            var bbox = this.getBBox(1);
+        }
+        cx = cx == null ? bbox.x + bbox.width / 2 : cx;
+        cy = cy == null ? bbox.y + bbox.height / 2 : cy;
+    
+        this.transform(this._.transform.concat([["s", sx, sy, cx, cy]]));
+        this._.dirtyT = 1;
+        return this;
+    };
+    elproto.hide = function () {
+        !this.removed && (this.node.style.display = "none");
+        return this;
+    };
+    elproto.show = function () {
+        !this.removed && (this.node.style.display = E);
+        return this;
+    };
+    // Needed to fix the vml setViewBox issues
+    elproto.auxGetBBox = R.el.getBBox;
+    elproto.getBBox = function(){
+      var b = this.auxGetBBox();
+      if (this.paper && this.paper._viewBoxShift)
+      {
+        var c = {};
+        var z = 1/this.paper._viewBoxShift.scale;
+        c.x = b.x - this.paper._viewBoxShift.dx;
+        c.x *= z;
+        c.y = b.y - this.paper._viewBoxShift.dy;
+        c.y *= z;
+        c.width  = b.width  * z;
+        c.height = b.height * z;
+        c.x2 = c.x + c.width;
+        c.y2 = c.y + c.height;
+        return c;
+      }
+      return b;
+    };
+    elproto._getBBox = function () {
+        if (this.removed) {
+            return {};
+        }
+        return {
+            x: this.X + (this.bbx || 0) - this.W / 2,
+            y: this.Y - this.H,
+            width: this.W,
+            height: this.H
+        };
+    };
+    elproto.remove = function () {
+        if (this.removed || !this.node.parentNode) {
+            return;
+        }
+        this.paper.__set__ && this.paper.__set__.exclude(this);
+        R.eve.unbind("raphael.*.*." + this.id);
+        R._tear(this, this.paper);
+        this.node.parentNode.removeChild(this.node);
+        this.shape && this.shape.parentNode.removeChild(this.shape);
+        for (var i in this) {
+            this[i] = typeof this[i] == "function" ? R._removedFactory(i) : null;
+        }
+        this.removed = true;
+    };
+    elproto.attr = function (name, value) {
+        if (this.removed) {
+            return this;
+        }
+        if (name == null) {
+            var res = {};
+            for (var a in this.attrs) if (this.attrs[has](a)) {
+                res[a] = this.attrs[a];
+            }
+            res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
+            res.transform = this._.transform;
+            return res;
+        }
+        if (value == null && R.is(name, "string")) {
+            if (name == fillString && this.attrs.fill == "none" && this.attrs.gradient) {
+                return this.attrs.gradient;
+            }
+            var names = name.split(separator),
+                out = {};
+            for (var i = 0, ii = names.length; i < ii; i++) {
+                name = names[i];
+                if (name in this.attrs) {
+                    out[name] = this.attrs[name];
+                } else if (R.is(this.paper.customAttributes[name], "function")) {
+                    out[name] = this.paper.customAttributes[name].def;
+                } else {
+                    out[name] = R._availableAttrs[name];
+                }
+            }
+            return ii - 1 ? out : out[names[0]];
+        }
+        if (this.attrs && value == null && R.is(name, "array")) {
+            out = {};
+            for (i = 0, ii = name.length; i < ii; i++) {
+                out[name[i]] = this.attr(name[i]);
+            }
+            return out;
+        }
+        var params;
+        if (value != null) {
+            params = {};
+            params[name] = value;
+        }
+        value == null && R.is(name, "object") && (params = name);
+        for (var key in params) {
+            eve("raphael.attr." + key + "." + this.id, this, params[key]);
+        }
+        if (params) {
+            for (key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) {
+                var par = this.paper.customAttributes[key].apply(this, [].concat(params[key]));
+                this.attrs[key] = params[key];
+                for (var subkey in par) if (par[has](subkey)) {
+                    params[subkey] = par[subkey];
+                }
+            }
+            // this.paper.canvas.style.display = "none";
+            if (params.text && this.type == "text") {
+                this.textpath.string = params.text;
+            }
+            setFillAndStroke(this, params);
+            // this.paper.canvas.style.display = E;
+        }
+        return this;
+    };
+    elproto.toFront = function () {
+        !this.removed && this.node.parentNode.appendChild(this.node);
+        this.paper && this.paper.top != this && R._tofront(this, this.paper);
+        return this;
+    };
+    elproto.toBack = function () {
+        if (this.removed) {
+            return this;
+        }
+        if (this.node.parentNode.firstChild != this.node) {
+            this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild);
+            R._toback(this, this.paper);
+        }
+        return this;
+    };
+    elproto.insertAfter = function (element) {
+        if (this.removed) {
+            return this;
+        }
+        if (element.constructor == R.st.constructor) {
+            element = element[element.length - 1];
+        }
+        if (element.node.nextSibling) {
+            element.node.parentNode.insertBefore(this.node, element.node.nextSibling);
+        } else {
+            element.node.parentNode.appendChild(this.node);
+        }
+        R._insertafter(this, element, this.paper);
+        return this;
+    };
+    elproto.insertBefore = function (element) {
+        if (this.removed) {
+            return this;
+        }
+        if (element.constructor == R.st.constructor) {
+            element = element[0];
+        }
+        element.node.parentNode.insertBefore(this.node, element.node);
+        R._insertbefore(this, element, this.paper);
+        return this;
+    };
+    elproto.blur = function (size) {
+        var s = this.node.runtimeStyle,
+            f = s.filter;
+        f = f.replace(blurregexp, E);
+        if (+size !== 0) {
+            this.attrs.blur = size;
+            s.filter = f + S + ms + ".Blur(pixelradius=" + (+size || 1.5) + ")";
+            s.margin = R.format("-{0}px 0 0 -{0}px", round(+size || 1.5));
+        } else {
+            s.filter = f;
+            s.margin = 0;
+            delete this.attrs.blur;
+        }
+        return this;
+    };
+
+    R._engine.path = function (pathString, vml) {
+        var el = createNode("shape");
+        el.style.cssText = cssDot;
+        el.coordsize = zoom + S + zoom;
+        el.coordorigin = vml.coordorigin;
+        var p = new Element(el, vml),
+            attr = {fill: "none", stroke: "#000"};
+        pathString && (attr.path = pathString);
+        p.type = "path";
+        p.path = [];
+        p.Path = E;
+        setFillAndStroke(p, attr);
+        vml.canvas.appendChild(el);
+        var skew = createNode("skew");
+        skew.on = true;
+        el.appendChild(skew);
+        p.skew = skew;
+        p.transform(E);
+        return p;
+    };
+    R._engine.rect = function (vml, x, y, w, h, r) {
+        var path = R._rectPath(x, y, w, h, r),
+            res = vml.path(path),
+            a = res.attrs;
+        res.X = a.x = x;
+        res.Y = a.y = y;
+        res.W = a.width = w;
+        res.H = a.height = h;
+        a.r = r;
+        a.path = path;
+        res.type = "rect";
+        return res;
+    };
+    R._engine.ellipse = function (vml, x, y, rx, ry) {
+        var res = vml.path(),
+            a = res.attrs;
+        res.X = x - rx;
+        res.Y = y - ry;
+        res.W = rx * 2;
+        res.H = ry * 2;
+        res.type = "ellipse";
+        setFillAndStroke(res, {
+            cx: x,
+            cy: y,
+            rx: rx,
+            ry: ry
+        });
+        return res;
+    };
+    R._engine.circle = function (vml, x, y, r) {
+        var res = vml.path(),
+            a = res.attrs;
+        res.X = x - r;
+        res.Y = y - r;
+        res.W = res.H = r * 2;
+        res.type = "circle";
+        setFillAndStroke(res, {
+            cx: x,
+            cy: y,
+            r: r
+        });
+        return res;
+    };
+    R._engine.image = function (vml, src, x, y, w, h) {
+        var path = R._rectPath(x, y, w, h),
+            res = vml.path(path).attr({stroke: "none"}),
+            a = res.attrs,
+            node = res.node,
+            fill = node.getElementsByTagName(fillString)[0];
+        a.src = src;
+        res.X = a.x = x;
+        res.Y = a.y = y;
+        res.W = a.width = w;
+        res.H = a.height = h;
+        a.path = path;
+        res.type = "image";
+        fill.parentNode == node && node.removeChild(fill);
+        fill.rotate = true;
+        fill.src = src;
+        fill.type = "tile";
+        res._.fillpos = [x, y];
+        res._.fillsize = [w, h];
+        node.appendChild(fill);
+        setCoords(res, 1, 1, 0, 0, 0);
+        return res;
+    };
+    R._engine.text = function (vml, x, y, text) {
+        var el = createNode("shape"),
+            path = createNode("path"),
+            o = createNode("textpath");
+        x = x || 0;
+        y = y || 0;
+        text = text || "";
+        path.v = R.format("m{0},{1}l{2},{1}", round(x * zoom), round(y * zoom), round(x * zoom) + 1);
+        path.textpathok = true;
+        o.string = Str(text);
+        o.on = true;
+        el.style.cssText = cssDot;
+        el.coordsize = zoom + S + zoom;
+        el.coordorigin = "0 0";
+        var p = new Element(el, vml),
+            attr = {
+                fill: "#000",
+                stroke: "none",
+                font: R._availableAttrs.font,
+                text: text
+            };
+        p.shape = el;
+        p.path = path;
+        p.textpath = o;
+        p.type = "text";
+        p.attrs.text = Str(text);
+        p.attrs.x = x;
+        p.attrs.y = y;
+        p.attrs.w = 1;
+        p.attrs.h = 1;
+        setFillAndStroke(p, attr);
+        el.appendChild(o);
+        el.appendChild(path);
+        vml.canvas.appendChild(el);
+        var skew = createNode("skew");
+        skew.on = true;
+        el.appendChild(skew);
+        p.skew = skew;
+        p.transform(E);
+        return p;
+    };
+    R._engine.setSize = function (width, height) {
+        var cs = this.canvas.style;
+        this.width = width;
+        this.height = height;
+        width == +width && (width += "px");
+        height == +height && (height += "px");
+        cs.width = width;
+        cs.height = height;
+        cs.clip = "rect(0 " + width + " " + height + " 0)";
+        if (this._viewBox) {
+            R._engine.setViewBox.apply(this, this._viewBox);
+        }
+        return this;
+    };
+    R._engine.setViewBox = function (x, y, w, h, fit) {
+        R.eve("raphael.setViewBox", this, this._viewBox, [x, y, w, h, fit]);
+        var paperSize = this.getSize(),
+            width = paperSize.width,
+            height = paperSize.height,
+            H, W;
+        if (fit) {
+            H = height / h;
+            W = width / w;
+            if (w * H < width) {
+                x -= (width - w * H) / 2 / H;
+            }
+            if (h * W < height) {
+                y -= (height - h * W) / 2 / W;
+            }
+        }
+        this._viewBox = [x, y, w, h, !!fit];
+        this._viewBoxShift = {
+            dx: -x,
+            dy: -y,
+            scale: paperSize
+        };
+        this.forEach(function (el) {
+            el.transform("...");
+        });
+        return this;
+    };
+    var createNode;
+    R._engine.initWin = function (win) {
+            var doc = win.document;
+            if (doc.styleSheets.length < 31) {
+                doc.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
+            } else {
+                // no more room, add to the existing one
+                // http://msdn.microsoft.com/en-us/library/ms531194%28VS.85%29.aspx
+                doc.styleSheets[0].addRule(".rvml", "behavior:url(#default#VML)");
+            }
+            try {
+                !doc.namespaces.rvml && doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
+                createNode = function (tagName) {
+                    return doc.createElement('<rvml:' + tagName + ' class="rvml">');
+                };
+            } catch (e) {
+                createNode = function (tagName) {
+                    return doc.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
+                };
+            }
+        };
+    R._engine.initWin(R._g.win);
+    R._engine.create = function () {
+        var con = R._getContainer.apply(0, arguments),
+            container = con.container,
+            height = con.height,
+            s,
+            width = con.width,
+            x = con.x,
+            y = con.y;
+        if (!container) {
+            throw new Error("VML container not found.");
+        }
+        var res = new R._Paper,
+            c = res.canvas = R._g.doc.createElement("div"),
+            cs = c.style;
+        x = x || 0;
+        y = y || 0;
+        width = width || 512;
+        height = height || 342;
+        res.width = width;
+        res.height = height;
+        width == +width && (width += "px");
+        height == +height && (height += "px");
+        res.coordsize = zoom * 1e3 + S + zoom * 1e3;
+        res.coordorigin = "0 0";
+        res.span = R._g.doc.createElement("span");
+        res.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;";
+        c.appendChild(res.span);
+        cs.cssText = R.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden", width, height);
+        if (container == 1) {
+            R._g.doc.body.appendChild(c);
+            cs.left = x + "px";
+            cs.top = y + "px";
+            cs.position = "absolute";
+        } else {
+            if (container.firstChild) {
+                container.insertBefore(c, container.firstChild);
+            } else {
+                container.appendChild(c);
+            }
+        }
+        res.renderfix = function () {};
+        return res;
+    };
+    R.prototype.clear = function () {
+        R.eve("raphael.clear", this);
+        this.canvas.innerHTML = E;
+        this.span = R._g.doc.createElement("span");
+        this.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";
+        this.canvas.appendChild(this.span);
+        this.bottom = this.top = null;
+    };
+    R.prototype.remove = function () {
+        R.eve("raphael.remove", this);
+        this.canvas.parentNode.removeChild(this.canvas);
+        for (var i in this) {
+            this[i] = typeof this[i] == "function" ? R._removedFactory(i) : null;
+        }
+        return true;
+    };
+
+    var setproto = R.st;
+    for (var method in elproto) if (elproto[has](method) && !setproto[has](method)) {
+        setproto[method] = (function (methodname) {
+            return function () {
+                var arg = arguments;
+                return this.forEach(function (el) {
+                    el[methodname].apply(el, arg);
+                });
+            };
+        })(method);
+    }
+})();
+
+    // EXPOSE
+    // SVG and VML are appended just before the EXPOSE line
+    // Even with AMD, Raphael should be defined globally
+    oldRaphael.was ? (g.win.Raphael = R) : (Raphael = R);
+
+    if(typeof exports == "object"){
+        module.exports = R;
+    }
+    return R;
+}));
diff --git a/vendor/assets/javascripts/task_list.js.coffee b/vendor/assets/javascripts/task_list.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..584751af8ea8e100974efdc5a06137d151626858
--- /dev/null
+++ b/vendor/assets/javascripts/task_list.js.coffee
@@ -0,0 +1,258 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 GitHub, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+# TaskList Behavior
+#
+#= provides tasklist:enabled
+#= provides tasklist:disabled
+#= provides tasklist:change
+#= provides tasklist:changed
+#
+#
+# Enables Task List update behavior.
+#
+# ### Example Markup
+#
+#   <div class="js-task-list-container">
+#     <ul class="task-list">
+#       <li class="task-list-item">
+#         <input type="checkbox" class="js-task-list-item-checkbox" disabled />
+#         text
+#       </li>
+#     </ul>
+#     <form>
+#       <textarea class="js-task-list-field">- [ ] text</textarea>
+#     </form>
+#   </div>
+#
+# ### Specification
+#
+# TaskLists MUST be contained in a `(div).js-task-list-container`.
+#
+# TaskList Items SHOULD be an a list (`UL`/`OL`) element.
+#
+# Task list items MUST match `(input).task-list-item-checkbox` and MUST be
+# `disabled` by default.
+#
+# TaskLists MUST have a `(textarea).js-task-list-field` form element whose
+# `value` attribute is the source (Markdown) to be udpated. The source MUST
+# follow the syntax guidelines.
+#
+# TaskList updates trigger `tasklist:change` events. If the change is
+# successful, `tasklist:changed` is fired. The change can be canceled.
+#
+# jQuery is required.
+#
+# ### Methods
+#
+# `.taskList('enable')` or `.taskList()`
+#
+# Enables TaskList updates for the container.
+#
+# `.taskList('disable')`
+#
+# Disables TaskList updates for the container.
+#
+## ### Events
+#
+# `tasklist:enabled`
+#
+# Fired when the TaskList is enabled.
+#
+# * **Synchronicity** Sync
+# * **Bubbles** Yes
+# * **Cancelable** No
+# * **Target** `.js-task-list-container`
+#
+# `tasklist:disabled`
+#
+# Fired when the TaskList is disabled.
+#
+# * **Synchronicity** Sync
+# * **Bubbles** Yes
+# * **Cancelable** No
+# * **Target** `.js-task-list-container`
+#
+# `tasklist:change`
+#
+# Fired before the TaskList item change takes affect.
+#
+# * **Synchronicity** Sync
+# * **Bubbles** Yes
+# * **Cancelable** Yes
+# * **Target** `.js-task-list-field`
+#
+# `tasklist:changed`
+#
+# Fired once the TaskList item change has taken affect.
+#
+# * **Synchronicity** Sync
+# * **Bubbles** Yes
+# * **Cancelable** No
+# * **Target** `.js-task-list-field`
+#
+# ### NOTE
+#
+# Task list checkboxes are rendered as disabled by default because rendered
+# user content is cached without regard for the viewer.
+
+incomplete = "[ ]"
+complete   = "[x]"
+
+# Escapes the String for regular expression matching.
+escapePattern = (str) ->
+  str.
+    replace(/([\[\]])/g, "\\$1"). # escape square brackets
+    replace(/\s/, "\\s").         # match all white space
+    replace("x", "[xX]")          # match all cases
+
+incompletePattern = ///
+  #{escapePattern(incomplete)}
+///
+completePattern = ///
+  #{escapePattern(complete)}
+///
+
+# Pattern used to identify all task list items.
+# Useful when you need iterate over all items.
+itemPattern = ///
+  ^
+  (?:                     # prefix, consisting of
+    \s*                   # optional leading whitespace
+    (?:>\s*)*             # zero or more blockquotes
+    (?:[-+*]|(?:\d+\.))   # list item indicator
+  )
+  \s*                     # optional whitespace prefix
+  (                       # checkbox
+    #{escapePattern(complete)}|
+    #{escapePattern(incomplete)}
+  )
+  \s+                     # is followed by whitespace
+  (?!
+    \(.*?\)               # is not part of a [foo](url) link
+  )
+  (?=                     # and is followed by zero or more links
+    (?:\[.*?\]\s*(?:\[.*?\]|\(.*?\))\s*)*
+    (?:[^\[]|$)           # and either a non-link or the end of the string
+  )
+///
+
+# Used to filter out code fences from the source for comparison only.
+# http://rubular.com/r/x5EwZVrloI
+# Modified slightly due to issues with JS
+codeFencesPattern = ///
+  ^`{3}           # ```
+    (?:\s*\w+)?   # followed by optional language
+    [\S\s]        # whitespace
+  .*              # code
+  [\S\s]          # whitespace
+  ^`{3}$          # ```
+///mg
+
+# Used to filter out potential mismatches (items not in lists).
+# http://rubular.com/r/OInl6CiePy
+itemsInParasPattern = ///
+  ^
+  (
+    #{escapePattern(complete)}|
+    #{escapePattern(incomplete)}
+  )
+  .+
+  $
+///g
+
+# Given the source text, updates the appropriate task list item to match the
+# given checked value.
+#
+# Returns the updated String text.
+updateTaskListItem = (source, itemIndex, checked) ->
+  clean = source.replace(/\r/g, '').replace(codeFencesPattern, '').
+    replace(itemsInParasPattern, '').split("\n")
+  index = 0
+  result = for line in source.split("\n")
+    if line in clean && line.match(itemPattern)
+      index += 1
+      if index == itemIndex
+        line =
+          if checked
+            line.replace(incompletePattern, complete)
+          else
+            line.replace(completePattern, incomplete)
+    line
+  result.join("\n")
+
+# Updates the $field value to reflect the state of $item.
+# Triggers the `tasklist:change` event before the value has changed, and fires
+# a `tasklist:changed` event once the value has changed.
+updateTaskList = ($item) ->
+  $container = $item.closest '.js-task-list-container'
+  $field     = $container.find '.js-task-list-field'
+  index      = 1 + $container.find('.task-list-item-checkbox').index($item)
+  checked    = $item.prop 'checked'
+
+  event = $.Event 'tasklist:change'
+  $field.trigger event, [index, checked]
+
+  unless event.isDefaultPrevented()
+    $field.val updateTaskListItem($field.val(), index, checked)
+    $field.trigger 'change'
+    $field.trigger 'tasklist:changed', [index, checked]
+
+# When the task list item checkbox is updated, submit the change
+$(document).on 'change', '.task-list-item-checkbox', ->
+  updateTaskList $(this)
+
+# Enables TaskList item changes.
+enableTaskList = ($container) ->
+  if $container.find('.js-task-list-field').length > 0
+    $container.
+      find('.task-list-item').addClass('enabled').
+      find('.task-list-item-checkbox').attr('disabled', null)
+    $container.addClass('is-task-list-enabled').
+      trigger 'tasklist:enabled'
+
+# Enables a collection of TaskList containers.
+enableTaskLists = ($containers) ->
+  for container in $containers
+    enableTaskList $(container)
+
+# Disable TaskList item changes.
+disableTaskList = ($container) ->
+  $container.
+    find('.task-list-item').removeClass('enabled').
+    find('.task-list-item-checkbox').attr('disabled', 'disabled')
+  $container.removeClass('is-task-list-enabled').
+    trigger 'tasklist:disabled'
+
+# Disables a collection of TaskList containers.
+disableTaskLists = ($containers) ->
+  for container in $containers
+    disableTaskList $(container)
+
+$.fn.taskList = (method) ->
+  $container = $(this).closest('.js-task-list-container')
+
+  methods =
+    enable: enableTaskLists
+    disable: disableTaskLists
+
+  methods[method || 'enable']($container)
diff --git a/vendor/assets/javascripts/u2f.js b/vendor/assets/javascripts/u2f.js
new file mode 100644
index 0000000000000000000000000000000000000000..e666b1360514dabcf4573ab0ec3fb6339d15c649
--- /dev/null
+++ b/vendor/assets/javascripts/u2f.js
@@ -0,0 +1,748 @@
+//Copyright 2014-2015 Google Inc. All rights reserved.
+
+//Use of this source code is governed by a BSD-style
+//license that can be found in the LICENSE file or at
+//https://developers.google.com/open-source/licenses/bsd
+
+/**
+ * @fileoverview The U2F api.
+ */
+'use strict';
+
+
+/**
+ * Namespace for the U2F api.
+ * @type {Object}
+ */
+var u2f = u2f || {};
+
+/**
+ * FIDO U2F Javascript API Version
+ * @number
+ */
+var js_api_version;
+
+/**
+ * The U2F extension id
+ * @const {string}
+ */
+// The Chrome packaged app extension ID.
+// Uncomment this if you want to deploy a server instance that uses
+// the package Chrome app and does not require installing the U2F Chrome extension.
+u2f.EXTENSION_ID = 'kmendfapggjehodndflmmgagdbamhnfd';
+// The U2F Chrome extension ID.
+// Uncomment this if you want to deploy a server instance that uses
+// the U2F Chrome extension to authenticate.
+// u2f.EXTENSION_ID = 'pfboblefjcgdjicmnffhdgionmgcdmne';
+
+
+/**
+ * Message types for messsages to/from the extension
+ * @const
+ * @enum {string}
+ */
+u2f.MessageTypes = {
+    'U2F_REGISTER_REQUEST': 'u2f_register_request',
+    'U2F_REGISTER_RESPONSE': 'u2f_register_response',
+    'U2F_SIGN_REQUEST': 'u2f_sign_request',
+    'U2F_SIGN_RESPONSE': 'u2f_sign_response',
+    'U2F_GET_API_VERSION_REQUEST': 'u2f_get_api_version_request',
+    'U2F_GET_API_VERSION_RESPONSE': 'u2f_get_api_version_response'
+};
+
+
+/**
+ * Response status codes
+ * @const
+ * @enum {number}
+ */
+u2f.ErrorCodes = {
+    'OK': 0,
+    'OTHER_ERROR': 1,
+    'BAD_REQUEST': 2,
+    'CONFIGURATION_UNSUPPORTED': 3,
+    'DEVICE_INELIGIBLE': 4,
+    'TIMEOUT': 5
+};
+
+
+/**
+ * A message for registration requests
+ * @typedef {{
+ *   type: u2f.MessageTypes,
+ *   appId: ?string,
+ *   timeoutSeconds: ?number,
+ *   requestId: ?number
+ * }}
+ */
+u2f.U2fRequest;
+
+
+/**
+ * A message for registration responses
+ * @typedef {{
+ *   type: u2f.MessageTypes,
+ *   responseData: (u2f.Error | u2f.RegisterResponse | u2f.SignResponse),
+ *   requestId: ?number
+ * }}
+ */
+u2f.U2fResponse;
+
+
+/**
+ * An error object for responses
+ * @typedef {{
+ *   errorCode: u2f.ErrorCodes,
+ *   errorMessage: ?string
+ * }}
+ */
+u2f.Error;
+
+/**
+ * Data object for a single sign request.
+ * @typedef {enum {BLUETOOTH_RADIO, BLUETOOTH_LOW_ENERGY, USB, NFC}}
+ */
+u2f.Transport;
+
+
+/**
+ * Data object for a single sign request.
+ * @typedef {Array<u2f.Transport>}
+ */
+u2f.Transports;
+
+/**
+ * Data object for a single sign request.
+ * @typedef {{
+ *   version: string,
+ *   challenge: string,
+ *   keyHandle: string,
+ *   appId: string
+ * }}
+ */
+u2f.SignRequest;
+
+
+/**
+ * Data object for a sign response.
+ * @typedef {{
+ *   keyHandle: string,
+ *   signatureData: string,
+ *   clientData: string
+ * }}
+ */
+u2f.SignResponse;
+
+
+/**
+ * Data object for a registration request.
+ * @typedef {{
+ *   version: string,
+ *   challenge: string
+ * }}
+ */
+u2f.RegisterRequest;
+
+
+/**
+ * Data object for a registration response.
+ * @typedef {{
+ *   version: string,
+ *   keyHandle: string,
+ *   transports: Transports,
+ *   appId: string
+ * }}
+ */
+u2f.RegisterResponse;
+
+
+/**
+ * Data object for a registered key.
+ * @typedef {{
+ *   version: string,
+ *   keyHandle: string,
+ *   transports: ?Transports,
+ *   appId: ?string
+ * }}
+ */
+u2f.RegisteredKey;
+
+
+/**
+ * Data object for a get API register response.
+ * @typedef {{
+ *   js_api_version: number
+ * }}
+ */
+u2f.GetJsApiVersionResponse;
+
+
+//Low level MessagePort API support
+
+/**
+ * Sets up a MessagePort to the U2F extension using the
+ * available mechanisms.
+ * @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback
+ */
+u2f.getMessagePort = function(callback) {
+    if (typeof chrome != 'undefined' && chrome.runtime) {
+        // The actual message here does not matter, but we need to get a reply
+        // for the callback to run. Thus, send an empty signature request
+        // in order to get a failure response.
+        var msg = {
+            type: u2f.MessageTypes.U2F_SIGN_REQUEST,
+            signRequests: []
+        };
+        chrome.runtime.sendMessage(u2f.EXTENSION_ID, msg, function() {
+            if (!chrome.runtime.lastError) {
+                // We are on a whitelisted origin and can talk directly
+                // with the extension.
+                u2f.getChromeRuntimePort_(callback);
+            } else {
+                // chrome.runtime was available, but we couldn't message
+                // the extension directly, use iframe
+                u2f.getIframePort_(callback);
+            }
+        });
+    } else if (u2f.isAndroidChrome_()) {
+        u2f.getAuthenticatorPort_(callback);
+    } else if (u2f.isIosChrome_()) {
+        u2f.getIosPort_(callback);
+    } else {
+        // chrome.runtime was not available at all, which is normal
+        // when this origin doesn't have access to any extensions.
+        u2f.getIframePort_(callback);
+    }
+};
+
+/**
+ * Detect chrome running on android based on the browser's useragent.
+ * @private
+ */
+u2f.isAndroidChrome_ = function() {
+    var userAgent = navigator.userAgent;
+    return userAgent.indexOf('Chrome') != -1 &&
+        userAgent.indexOf('Android') != -1;
+};
+
+/**
+ * Detect chrome running on iOS based on the browser's platform.
+ * @private
+ */
+u2f.isIosChrome_ = function() {
+    return $.inArray(navigator.platform, ["iPhone", "iPad", "iPod"]) > -1;
+};
+
+/**
+ * Connects directly to the extension via chrome.runtime.connect.
+ * @param {function(u2f.WrappedChromeRuntimePort_)} callback
+ * @private
+ */
+u2f.getChromeRuntimePort_ = function(callback) {
+    var port = chrome.runtime.connect(u2f.EXTENSION_ID,
+        {'includeTlsChannelId': true});
+    setTimeout(function() {
+        callback(new u2f.WrappedChromeRuntimePort_(port));
+    }, 0);
+};
+
+/**
+ * Return a 'port' abstraction to the Authenticator app.
+ * @param {function(u2f.WrappedAuthenticatorPort_)} callback
+ * @private
+ */
+u2f.getAuthenticatorPort_ = function(callback) {
+    setTimeout(function() {
+        callback(new u2f.WrappedAuthenticatorPort_());
+    }, 0);
+};
+
+/**
+ * Return a 'port' abstraction to the iOS client app.
+ * @param {function(u2f.WrappedIosPort_)} callback
+ * @private
+ */
+u2f.getIosPort_ = function(callback) {
+    setTimeout(function() {
+        callback(new u2f.WrappedIosPort_());
+    }, 0);
+};
+
+/**
+ * A wrapper for chrome.runtime.Port that is compatible with MessagePort.
+ * @param {Port} port
+ * @constructor
+ * @private
+ */
+u2f.WrappedChromeRuntimePort_ = function(port) {
+    this.port_ = port;
+};
+
+/**
+ * Format and return a sign request compliant with the JS API version supported by the extension.
+ * @param {Array<u2f.SignRequest>} signRequests
+ * @param {number} timeoutSeconds
+ * @param {number} reqId
+ * @return {Object}
+ */
+u2f.formatSignRequest_ =
+    function(appId, challenge, registeredKeys, timeoutSeconds, reqId) {
+        if (js_api_version === undefined || js_api_version < 1.1) {
+            // Adapt request to the 1.0 JS API
+            var signRequests = [];
+            for (var i = 0; i < registeredKeys.length; i++) {
+                signRequests[i] = {
+                    version: registeredKeys[i].version,
+                    challenge: challenge,
+                    keyHandle: registeredKeys[i].keyHandle,
+                    appId: appId
+                };
+            }
+            return {
+                type: u2f.MessageTypes.U2F_SIGN_REQUEST,
+                signRequests: signRequests,
+                timeoutSeconds: timeoutSeconds,
+                requestId: reqId
+            };
+        }
+        // JS 1.1 API
+        return {
+            type: u2f.MessageTypes.U2F_SIGN_REQUEST,
+            appId: appId,
+            challenge: challenge,
+            registeredKeys: registeredKeys,
+            timeoutSeconds: timeoutSeconds,
+            requestId: reqId
+        };
+    };
+
+/**
+ * Format and return a register request compliant with the JS API version supported by the extension..
+ * @param {Array<u2f.SignRequest>} signRequests
+ * @param {Array<u2f.RegisterRequest>} signRequests
+ * @param {number} timeoutSeconds
+ * @param {number} reqId
+ * @return {Object}
+ */
+u2f.formatRegisterRequest_ =
+    function(appId, registeredKeys, registerRequests, timeoutSeconds, reqId) {
+        if (js_api_version === undefined || js_api_version < 1.1) {
+            // Adapt request to the 1.0 JS API
+            for (var i = 0; i < registerRequests.length; i++) {
+                registerRequests[i].appId = appId;
+            }
+            var signRequests = [];
+            for (var i = 0; i < registeredKeys.length; i++) {
+                signRequests[i] = {
+                    version: registeredKeys[i].version,
+                    challenge: registerRequests[0],
+                    keyHandle: registeredKeys[i].keyHandle,
+                    appId: appId
+                };
+            }
+            return {
+                type: u2f.MessageTypes.U2F_REGISTER_REQUEST,
+                signRequests: signRequests,
+                registerRequests: registerRequests,
+                timeoutSeconds: timeoutSeconds,
+                requestId: reqId
+            };
+        }
+        // JS 1.1 API
+        return {
+            type: u2f.MessageTypes.U2F_REGISTER_REQUEST,
+            appId: appId,
+            registerRequests: registerRequests,
+            registeredKeys: registeredKeys,
+            timeoutSeconds: timeoutSeconds,
+            requestId: reqId
+        };
+    };
+
+
+/**
+ * Posts a message on the underlying channel.
+ * @param {Object} message
+ */
+u2f.WrappedChromeRuntimePort_.prototype.postMessage = function(message) {
+    this.port_.postMessage(message);
+};
+
+
+/**
+ * Emulates the HTML 5 addEventListener interface. Works only for the
+ * onmessage event, which is hooked up to the chrome.runtime.Port.onMessage.
+ * @param {string} eventName
+ * @param {function({data: Object})} handler
+ */
+u2f.WrappedChromeRuntimePort_.prototype.addEventListener =
+    function(eventName, handler) {
+        var name = eventName.toLowerCase();
+        if (name == 'message' || name == 'onmessage') {
+            this.port_.onMessage.addListener(function(message) {
+                // Emulate a minimal MessageEvent object
+                handler({'data': message});
+            });
+        } else {
+            console.error('WrappedChromeRuntimePort only supports onMessage');
+        }
+    };
+
+/**
+ * Wrap the Authenticator app with a MessagePort interface.
+ * @constructor
+ * @private
+ */
+u2f.WrappedAuthenticatorPort_ = function() {
+    this.requestId_ = -1;
+    this.requestObject_ = null;
+}
+
+/**
+ * Launch the Authenticator intent.
+ * @param {Object} message
+ */
+u2f.WrappedAuthenticatorPort_.prototype.postMessage = function(message) {
+    var intentUrl =
+        u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ +
+        ';S.request=' + encodeURIComponent(JSON.stringify(message)) +
+        ';end';
+    document.location = intentUrl;
+};
+
+/**
+ * Tells what type of port this is.
+ * @return {String} port type
+ */
+u2f.WrappedAuthenticatorPort_.prototype.getPortType = function() {
+    return "WrappedAuthenticatorPort_";
+};
+
+
+/**
+ * Emulates the HTML 5 addEventListener interface.
+ * @param {string} eventName
+ * @param {function({data: Object})} handler
+ */
+u2f.WrappedAuthenticatorPort_.prototype.addEventListener = function(eventName, handler) {
+    var name = eventName.toLowerCase();
+    if (name == 'message') {
+        var self = this;
+        /* Register a callback to that executes when
+         * chrome injects the response. */
+        window.addEventListener(
+            'message', self.onRequestUpdate_.bind(self, handler), false);
+    } else {
+        console.error('WrappedAuthenticatorPort only supports message');
+    }
+};
+
+/**
+ * Callback invoked  when a response is received from the Authenticator.
+ * @param function({data: Object}) callback
+ * @param {Object} message message Object
+ */
+u2f.WrappedAuthenticatorPort_.prototype.onRequestUpdate_ =
+    function(callback, message) {
+        var messageObject = JSON.parse(message.data);
+        var intentUrl = messageObject['intentURL'];
+
+        var errorCode = messageObject['errorCode'];
+        var responseObject = null;
+        if (messageObject.hasOwnProperty('data')) {
+            responseObject = /** @type {Object} */ (
+                JSON.parse(messageObject['data']));
+        }
+
+        callback({'data': responseObject});
+    };
+
+/**
+ * Base URL for intents to Authenticator.
+ * @const
+ * @private
+ */
+u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ =
+    'intent:#Intent;action=com.google.android.apps.authenticator.AUTHENTICATE';
+
+/**
+ * Wrap the iOS client app with a MessagePort interface.
+ * @constructor
+ * @private
+ */
+u2f.WrappedIosPort_ = function() {};
+
+/**
+ * Launch the iOS client app request
+ * @param {Object} message
+ */
+u2f.WrappedIosPort_.prototype.postMessage = function(message) {
+    var str = JSON.stringify(message);
+    var url = "u2f://auth?" + encodeURI(str);
+    location.replace(url);
+};
+
+/**
+ * Tells what type of port this is.
+ * @return {String} port type
+ */
+u2f.WrappedIosPort_.prototype.getPortType = function() {
+    return "WrappedIosPort_";
+};
+
+/**
+ * Emulates the HTML 5 addEventListener interface.
+ * @param {string} eventName
+ * @param {function({data: Object})} handler
+ */
+u2f.WrappedIosPort_.prototype.addEventListener = function(eventName, handler) {
+    var name = eventName.toLowerCase();
+    if (name !== 'message') {
+        console.error('WrappedIosPort only supports message');
+    }
+};
+
+/**
+ * Sets up an embedded trampoline iframe, sourced from the extension.
+ * @param {function(MessagePort)} callback
+ * @private
+ */
+u2f.getIframePort_ = function(callback) {
+    // Create the iframe
+    var iframeOrigin = 'chrome-extension://' + u2f.EXTENSION_ID;
+    var iframe = document.createElement('iframe');
+    iframe.src = iframeOrigin + '/u2f-comms.html';
+    iframe.setAttribute('style', 'display:none');
+    document.body.appendChild(iframe);
+
+    var channel = new MessageChannel();
+    var ready = function(message) {
+        if (message.data == 'ready') {
+            channel.port1.removeEventListener('message', ready);
+            callback(channel.port1);
+        } else {
+            console.error('First event on iframe port was not "ready"');
+        }
+    };
+    channel.port1.addEventListener('message', ready);
+    channel.port1.start();
+
+    iframe.addEventListener('load', function() {
+        // Deliver the port to the iframe and initialize
+        iframe.contentWindow.postMessage('init', iframeOrigin, [channel.port2]);
+    });
+};
+
+
+//High-level JS API
+
+/**
+ * Default extension response timeout in seconds.
+ * @const
+ */
+u2f.EXTENSION_TIMEOUT_SEC = 30;
+
+/**
+ * A singleton instance for a MessagePort to the extension.
+ * @type {MessagePort|u2f.WrappedChromeRuntimePort_}
+ * @private
+ */
+u2f.port_ = null;
+
+/**
+ * Callbacks waiting for a port
+ * @type {Array<function((MessagePort|u2f.WrappedChromeRuntimePort_))>}
+ * @private
+ */
+u2f.waitingForPort_ = [];
+
+/**
+ * A counter for requestIds.
+ * @type {number}
+ * @private
+ */
+u2f.reqCounter_ = 0;
+
+/**
+ * A map from requestIds to client callbacks
+ * @type {Object.<number,(function((u2f.Error|u2f.RegisterResponse))
+ *                       |function((u2f.Error|u2f.SignResponse)))>}
+ * @private
+ */
+u2f.callbackMap_ = {};
+
+/**
+ * Creates or retrieves the MessagePort singleton to use.
+ * @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback
+ * @private
+ */
+u2f.getPortSingleton_ = function(callback) {
+    if (u2f.port_) {
+        callback(u2f.port_);
+    } else {
+        if (u2f.waitingForPort_.length == 0) {
+            u2f.getMessagePort(function(port) {
+                u2f.port_ = port;
+                u2f.port_.addEventListener('message',
+                    /** @type {function(Event)} */ (u2f.responseHandler_));
+
+                // Careful, here be async callbacks. Maybe.
+                while (u2f.waitingForPort_.length)
+                    u2f.waitingForPort_.shift()(u2f.port_);
+            });
+        }
+        u2f.waitingForPort_.push(callback);
+    }
+};
+
+/**
+ * Handles response messages from the extension.
+ * @param {MessageEvent.<u2f.Response>} message
+ * @private
+ */
+u2f.responseHandler_ = function(message) {
+    var response = message.data;
+    var reqId = response['requestId'];
+    if (!reqId || !u2f.callbackMap_[reqId]) {
+        console.error('Unknown or missing requestId in response.');
+        return;
+    }
+    var cb = u2f.callbackMap_[reqId];
+    delete u2f.callbackMap_[reqId];
+    cb(response['responseData']);
+};
+
+/**
+ * Dispatches an array of sign requests to available U2F tokens.
+ * If the JS API version supported by the extension is unknown, it first sends a
+ * message to the extension to find out the supported API version and then it sends
+ * the sign request.
+ * @param {string=} appId
+ * @param {string=} challenge
+ * @param {Array<u2f.RegisteredKey>} registeredKeys
+ * @param {function((u2f.Error|u2f.SignResponse))} callback
+ * @param {number=} opt_timeoutSeconds
+ */
+u2f.sign = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {
+    if (js_api_version === undefined) {
+        // Send a message to get the extension to JS API version, then send the actual sign request.
+        u2f.getApiVersion(
+            function (response) {
+                js_api_version = response['js_api_version'] === undefined ? 0 : response['js_api_version'];
+                console.log("Extension JS API Version: ", js_api_version);
+                u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds);
+            });
+    } else {
+        // We know the JS API version. Send the actual sign request in the supported API version.
+        u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds);
+    }
+};
+
+/**
+ * Dispatches an array of sign requests to available U2F tokens.
+ * @param {string=} appId
+ * @param {string=} challenge
+ * @param {Array<u2f.RegisteredKey>} registeredKeys
+ * @param {function((u2f.Error|u2f.SignResponse))} callback
+ * @param {number=} opt_timeoutSeconds
+ */
+u2f.sendSignRequest = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {
+    u2f.getPortSingleton_(function(port) {
+        var reqId = ++u2f.reqCounter_;
+        u2f.callbackMap_[reqId] = callback;
+        var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?
+            opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC);
+        var req = u2f.formatSignRequest_(appId, challenge, registeredKeys, timeoutSeconds, reqId);
+        port.postMessage(req);
+    });
+};
+
+/**
+ * Dispatches register requests to available U2F tokens. An array of sign
+ * requests identifies already registered tokens.
+ * If the JS API version supported by the extension is unknown, it first sends a
+ * message to the extension to find out the supported API version and then it sends
+ * the register request.
+ * @param {string=} appId
+ * @param {Array<u2f.RegisterRequest>} registerRequests
+ * @param {Array<u2f.RegisteredKey>} registeredKeys
+ * @param {function((u2f.Error|u2f.RegisterResponse))} callback
+ * @param {number=} opt_timeoutSeconds
+ */
+u2f.register = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) {
+    if (js_api_version === undefined) {
+        // Send a message to get the extension to JS API version, then send the actual register request.
+        u2f.getApiVersion(
+            function (response) {
+                js_api_version = response['js_api_version'] === undefined ? 0: response['js_api_version'];
+                console.log("Extension JS API Version: ", js_api_version);
+                u2f.sendRegisterRequest(appId, registerRequests, registeredKeys,
+                    callback, opt_timeoutSeconds);
+            });
+    } else {
+        // We know the JS API version. Send the actual register request in the supported API version.
+        u2f.sendRegisterRequest(appId, registerRequests, registeredKeys,
+            callback, opt_timeoutSeconds);
+    }
+};
+
+/**
+ * Dispatches register requests to available U2F tokens. An array of sign
+ * requests identifies already registered tokens.
+ * @param {string=} appId
+ * @param {Array<u2f.RegisterRequest>} registerRequests
+ * @param {Array<u2f.RegisteredKey>} registeredKeys
+ * @param {function((u2f.Error|u2f.RegisterResponse))} callback
+ * @param {number=} opt_timeoutSeconds
+ */
+u2f.sendRegisterRequest = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) {
+    u2f.getPortSingleton_(function(port) {
+        var reqId = ++u2f.reqCounter_;
+        u2f.callbackMap_[reqId] = callback;
+        var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?
+            opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC);
+        var req = u2f.formatRegisterRequest_(
+            appId, registeredKeys, registerRequests, timeoutSeconds, reqId);
+        port.postMessage(req);
+    });
+};
+
+
+/**
+ * Dispatches a message to the extension to find out the supported
+ * JS API version.
+ * If the user is on a mobile phone and is thus using Google Authenticator instead
+ * of the Chrome extension, don't send the request and simply return 0.
+ * @param {function((u2f.Error|u2f.GetJsApiVersionResponse))} callback
+ * @param {number=} opt_timeoutSeconds
+ */
+u2f.getApiVersion = function(callback, opt_timeoutSeconds) {
+    u2f.getPortSingleton_(function(port) {
+        // If we are using Android Google Authenticator or iOS client app,
+        // do not fire an intent to ask which JS API version to use.
+        if (port.getPortType) {
+            var apiVersion;
+            switch (port.getPortType()) {
+                case 'WrappedIosPort_':
+                case 'WrappedAuthenticatorPort_':
+                    apiVersion = 1.1;
+                    break;
+
+                default:
+                    apiVersion = 0;
+                    break;
+            }
+            callback({ 'js_api_version': apiVersion });
+            return;
+        }
+        var reqId = ++u2f.reqCounter_;
+        u2f.callbackMap_[reqId] = callback;
+        var req = {
+            type: u2f.MessageTypes.U2F_GET_API_VERSION_REQUEST,
+            timeoutSeconds: (typeof opt_timeoutSeconds !== 'undefined' ?
+                opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC),
+            requestId: reqId
+        };
+        port.postMessage(req);
+    });
+};
\ No newline at end of file
diff --git a/vendor/gitignore/Actionscript.gitignore b/vendor/gitignore/Actionscript.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..11e612e985379084e7c6ce4bf12d6a5cd07b2743
--- /dev/null
+++ b/vendor/gitignore/Actionscript.gitignore
@@ -0,0 +1,19 @@
+# Build and Release Folders
+bin/
+bin-debug/
+bin-release/
+[Oo]bj/ # FlashDevelop obj
+[Bb]in/ # FlashDevelop bin
+
+# Other files and folders
+.settings/
+
+# Executables
+*.swf
+*.air
+*.ipa
+*.apk
+
+# Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties`
+# should NOT be excluded as they contain compiler settings and other important
+# information for Eclipse / Flash Builder.
diff --git a/vendor/gitignore/Ada.gitignore b/vendor/gitignore/Ada.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b4d703968a488445345202ef8d45a35cc802aa03
--- /dev/null
+++ b/vendor/gitignore/Ada.gitignore
@@ -0,0 +1,5 @@
+# Object file
+*.o
+
+# Ada Library Information
+*.ali
diff --git a/vendor/gitignore/Agda.gitignore b/vendor/gitignore/Agda.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..171a38976c10152af35e1ad8cf931f3488b894ed
--- /dev/null
+++ b/vendor/gitignore/Agda.gitignore
@@ -0,0 +1 @@
+*.agdai
diff --git a/vendor/gitignore/Android.gitignore b/vendor/gitignore/Android.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..a8368751267c19e45c935c0f3d17cb8322b62dd3
--- /dev/null
+++ b/vendor/gitignore/Android.gitignore
@@ -0,0 +1,39 @@
+# Built application files
+*.apk
+*.ap_
+
+# Files for the Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+out/
+
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+*.log
+
+# Android Studio Navigation editor temp files
+.navigation/
+
+# Android Studio captures folder
+captures/
+
+# Intellij
+*.iml
+
+# Keystore files
+*.jks
diff --git a/vendor/gitignore/AppEngine.gitignore b/vendor/gitignore/AppEngine.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..62273454531a136f13f5ce156157c03e243e8c2c
--- /dev/null
+++ b/vendor/gitignore/AppEngine.gitignore
@@ -0,0 +1,2 @@
+# Google App Engine generated folder
+appengine-generated/
diff --git a/vendor/gitignore/AppceleratorTitanium.gitignore b/vendor/gitignore/AppceleratorTitanium.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..3abea5597613e5baef43877d021dbcdf68048d17
--- /dev/null
+++ b/vendor/gitignore/AppceleratorTitanium.gitignore
@@ -0,0 +1,3 @@
+# Build folder and log file
+build/
+build.log
diff --git a/vendor/gitignore/ArchLinuxPackages.gitignore b/vendor/gitignore/ArchLinuxPackages.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b73905529f237733c3690a9355d4730c6c9e61a6
--- /dev/null
+++ b/vendor/gitignore/ArchLinuxPackages.gitignore
@@ -0,0 +1,13 @@
+*.tar
+*.tar.*
+*.jar
+*.exe
+*.msi
+*.zip
+*.tgz
+*.log
+*.log.*
+*.sig
+
+pkg/
+src/
diff --git a/vendor/gitignore/Autotools.gitignore b/vendor/gitignore/Autotools.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..1e9158e2a85f8972b26a901be4be6e1972088885
--- /dev/null
+++ b/vendor/gitignore/Autotools.gitignore
@@ -0,0 +1,18 @@
+# http://www.gnu.org/software/automake
+
+Makefile.in
+
+# http://www.gnu.org/software/autoconf
+
+/autom4te.cache
+/autoscan.log
+/autoscan-*.log
+/aclocal.m4
+/compile
+/config.h.in
+/configure
+/configure.scan
+/depcomp
+/install-sh
+/missing
+/stamp-h1
diff --git a/vendor/gitignore/C++.gitignore b/vendor/gitignore/C++.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b8bd0267bdf1a5e02793430b53cbbebd6c257f6f
--- /dev/null
+++ b/vendor/gitignore/C++.gitignore
@@ -0,0 +1,28 @@
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
diff --git a/vendor/gitignore/C.gitignore b/vendor/gitignore/C.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..f805e810e5c6e087791506b4e721958de3574ae4
--- /dev/null
+++ b/vendor/gitignore/C.gitignore
@@ -0,0 +1,33 @@
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
diff --git a/vendor/gitignore/CFWheels.gitignore b/vendor/gitignore/CFWheels.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..f2fec34ff897c8af9a339bb7ade95841229e3109
--- /dev/null
+++ b/vendor/gitignore/CFWheels.gitignore
@@ -0,0 +1,12 @@
+# unpacked plugin folders
+plugins/**/*
+
+# files directory where uploads go
+files
+
+# DBMigrate plugin: generated SQL
+db/sql
+
+# AssetBundler plugin: generated bundles
+javascripts/bundles
+stylesheets/bundles
diff --git a/vendor/gitignore/CMake.gitignore b/vendor/gitignore/CMake.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b558e9afa6da4591ea15d0b78a5fd9886eb8d877
--- /dev/null
+++ b/vendor/gitignore/CMake.gitignore
@@ -0,0 +1,6 @@
+CMakeCache.txt
+CMakeFiles
+CMakeScripts
+Makefile
+cmake_install.cmake
+install_manifest.txt
diff --git a/vendor/gitignore/CUDA.gitignore b/vendor/gitignore/CUDA.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..cb385db83feab198506af09a2de20526e2252979
--- /dev/null
+++ b/vendor/gitignore/CUDA.gitignore
@@ -0,0 +1,6 @@
+*.i
+*.ii
+*.gpu
+*.ptx
+*.cubin
+*.fatbin
diff --git a/vendor/gitignore/CakePHP.gitignore b/vendor/gitignore/CakePHP.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..c6597e4eabf8c5e9e6a6388ca786c7f507ddec00
--- /dev/null
+++ b/vendor/gitignore/CakePHP.gitignore
@@ -0,0 +1,25 @@
+# CakePHP 3
+
+/vendor/*
+/config/app.php
+
+/tmp/cache/models/*
+!/tmp/cache/models/empty
+/tmp/cache/persistent/*
+!/tmp/cache/persistent/empty
+/tmp/cache/views/*
+!/tmp/cache/views/empty
+/tmp/sessions/*
+!/tmp/sessions/empty
+/tmp/tests/*
+!/tmp/tests/empty
+
+/logs/*
+!/logs/empty
+
+# CakePHP 2
+
+/app/tmp/*
+/app/Config/core.php
+/app/Config/database.php
+/vendors/*
diff --git a/vendor/gitignore/ChefCookbook.gitignore b/vendor/gitignore/ChefCookbook.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..5ee7b7a9a1806b35853f7e836a0a7fde15aaf509
--- /dev/null
+++ b/vendor/gitignore/ChefCookbook.gitignore
@@ -0,0 +1,9 @@
+.vagrant
+/cookbooks
+
+# Bundler
+bin/*
+.bundle/*
+
+.kitchen/
+.kitchen.local.yml
diff --git a/vendor/gitignore/Clojure.gitignore b/vendor/gitignore/Clojure.gitignore
new file mode 120000
index 0000000000000000000000000000000000000000..7657a270c457f4d600c76f2a91775c90b730062d
--- /dev/null
+++ b/vendor/gitignore/Clojure.gitignore
@@ -0,0 +1 @@
+Leiningen.gitignore
\ No newline at end of file
diff --git a/vendor/gitignore/CodeIgniter.gitignore b/vendor/gitignore/CodeIgniter.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..0f77d9e1d175c90e6048385f689e35830506bc23
--- /dev/null
+++ b/vendor/gitignore/CodeIgniter.gitignore
@@ -0,0 +1,6 @@
+*/config/development
+*/logs/log-*.php
+!*/logs/index.html
+*/cache/*
+!*/cache/index.html
+!*/cache/.htaccess
diff --git a/vendor/gitignore/CommonLisp.gitignore b/vendor/gitignore/CommonLisp.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..4806e580b601c8ac5f557c98c5320af98638020f
--- /dev/null
+++ b/vendor/gitignore/CommonLisp.gitignore
@@ -0,0 +1,3 @@
+*.FASL
+*.fasl
+*.lisp-temp
diff --git a/vendor/gitignore/Composer.gitignore b/vendor/gitignore/Composer.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..c422267842408488b4af853105d157e9021c249b
--- /dev/null
+++ b/vendor/gitignore/Composer.gitignore
@@ -0,0 +1,6 @@
+composer.phar
+/vendor/
+
+# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
+# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
+# composer.lock
diff --git a/vendor/gitignore/Concrete5.gitignore b/vendor/gitignore/Concrete5.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..1fe53611e5d7baf0dbe9678343fc9795a6f660bc
--- /dev/null
+++ b/vendor/gitignore/Concrete5.gitignore
@@ -0,0 +1,4 @@
+config/site.php
+files/cache/*
+files/tmp/*
+.htaccess
diff --git a/vendor/gitignore/Coq.gitignore b/vendor/gitignore/Coq.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..d3083b3a605d974e7af5060029a58012d71c251b
--- /dev/null
+++ b/vendor/gitignore/Coq.gitignore
@@ -0,0 +1,3 @@
+*.vo
+*.glob
+*.v.d
diff --git a/vendor/gitignore/CraftCMS.gitignore b/vendor/gitignore/CraftCMS.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..a70d4772c46cb1fb34aad98c279080d456a25e4f
--- /dev/null
+++ b/vendor/gitignore/CraftCMS.gitignore
@@ -0,0 +1,3 @@
+# Craft Storage (cache) [http://buildwithcraft.com/help/craft-storage-gitignore]
+/craft/storage/*
+!/craft/storage/logo/*
\ No newline at end of file
diff --git a/vendor/gitignore/D.gitignore b/vendor/gitignore/D.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b4433f8a512390abfa1908a00f46a87e13c4e6f6
--- /dev/null
+++ b/vendor/gitignore/D.gitignore
@@ -0,0 +1,20 @@
+# Compiled Object files
+*.o
+*.obj
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Compiled Static libraries
+*.a
+*.lib
+
+# Executables
+*.exe
+
+# DUB
+.dub
+docs.json
+__dummy.html
diff --git a/vendor/gitignore/DM.gitignore b/vendor/gitignore/DM.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..ba5abdab83666220c8dbd5367c9cff1d4898222c
--- /dev/null
+++ b/vendor/gitignore/DM.gitignore
@@ -0,0 +1,5 @@
+*.dmb
+*.rsc
+*.int
+*.lk
+*.zip
diff --git a/vendor/gitignore/Dart.gitignore b/vendor/gitignore/Dart.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..7c280441649860d99e9df07c398b32012ea8fffd
--- /dev/null
+++ b/vendor/gitignore/Dart.gitignore
@@ -0,0 +1,27 @@
+# See https://www.dartlang.org/tools/private-files.html
+
+# Files and directories created by pub
+.buildlog
+.packages
+.project
+.pub/
+build/
+**/packages/
+
+# Files created by dart2js
+# (Most Dart developers will use pub build to compile Dart, use/modify these 
+#  rules if you intend to use dart2js directly
+#  Convention is to use extension '.dart.js' for Dart compiled to Javascript to
+#  differentiate from explicit Javascript files)
+*.dart.js
+*.part.js
+*.js.deps
+*.js.map
+*.info.json
+
+# Directory created by dartdoc
+doc/api/
+
+# Don't commit pubspec lock file 
+# (Library packages only! Remove pattern if developing an application package)
+pubspec.lock
diff --git a/vendor/gitignore/Delphi.gitignore b/vendor/gitignore/Delphi.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..19864c6bbefbdfa7da3dfba8c41b193faa245a7e
--- /dev/null
+++ b/vendor/gitignore/Delphi.gitignore
@@ -0,0 +1,66 @@
+# Uncomment these types if you want even more clean repository. But be careful.
+# It can make harm to an existing project source. Read explanations below.
+#
+# Resource files are binaries containing manifest, project icon and version info.
+# They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files.
+#*.res
+#
+# Type library file (binary). In old Delphi versions it should be stored.
+# Since Delphi 2009 it is produced from .ridl file and can safely be ignored.
+#*.tlb
+#
+# Diagram Portfolio file. Used by the diagram editor up to Delphi 7.
+# Uncomment this if you are not using diagrams or use newer Delphi version.
+#*.ddp
+#
+# Visual LiveBindings file. Added in Delphi XE2.
+# Uncomment this if you are not using LiveBindings Designer.
+#*.vlb
+#
+# Deployment Manager configuration file for your project. Added in Delphi XE2.
+# Uncomment this if it is not mobile development and you do not use remote debug feature.
+#*.deployproj
+# 
+# C++ object files produced when C/C++ Output file generation is configured.
+# Uncomment this if you are not using external objects (zlib library for example).
+#*.obj
+#
+
+# Delphi compiler-generated binaries (safe to delete)
+*.exe
+*.dll
+*.bpl
+*.bpi
+*.dcp
+*.so
+*.apk
+*.drc
+*.map
+*.dres
+*.rsm
+*.tds
+*.dcu
+*.lib
+*.a
+*.o
+*.ocx
+
+# Delphi autogenerated files (duplicated info)
+*.cfg
+*.hpp
+*Resource.rc
+
+# Delphi local files (user-specific info)
+*.local
+*.identcache
+*.projdata
+*.tvsconfig
+*.dsk
+
+# Delphi history and backups
+__history/
+__recovery/
+*.~*
+
+# Castalia statistics file (since XE7 Castalia is distributed with Delphi)
+*.stat
diff --git a/vendor/gitignore/Drupal.gitignore b/vendor/gitignore/Drupal.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..0d2fe537f46d8792cf05c1f371ab2df98eab208c
--- /dev/null
+++ b/vendor/gitignore/Drupal.gitignore
@@ -0,0 +1,36 @@
+# Ignore configuration files that may contain sensitive information.
+sites/*/*settings*.php
+
+# Ignore paths that contain generated content.
+files/
+sites/*/files
+sites/*/private
+
+# Ignore default text files
+robots.txt
+/CHANGELOG.txt
+/COPYRIGHT.txt
+/INSTALL*.txt
+/LICENSE.txt
+/MAINTAINERS.txt
+/UPGRADE.txt
+/README.txt
+sites/README.txt
+sites/all/modules/README.txt
+sites/all/themes/README.txt
+
+# Ignore everything but the "sites" folder ( for non core developer )
+.htaccess
+web.config
+authorize.php
+cron.php
+index.php
+install.php
+update.php
+xmlrpc.php
+/includes
+/misc
+/modules
+/profiles
+/scripts
+/themes
diff --git a/vendor/gitignore/EPiServer.gitignore b/vendor/gitignore/EPiServer.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..97037de743e26b47451a693b0c186564c669b4dc
--- /dev/null
+++ b/vendor/gitignore/EPiServer.gitignore
@@ -0,0 +1,4 @@
+######################
+## EPiServer Files
+######################
+*License.config
diff --git a/vendor/gitignore/Eagle.gitignore b/vendor/gitignore/Eagle.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..9ced12602664135a531962c2ad046eb92c3ffc56
--- /dev/null
+++ b/vendor/gitignore/Eagle.gitignore
@@ -0,0 +1,44 @@
+# Ignore list for Eagle, a PCB layout tool
+
+# Backup files
+*.s#?
+*.b#?
+*.l#?
+
+# Eagle project file
+# It contains a serial number and references to the file structure
+# on your computer.
+# comment the following line if you want to have your project file included.
+eagle.epf
+
+# Autorouter files
+*.pro
+*.job
+
+# CAM files
+*.$$$
+*.cmp
+*.ly2
+*.l15
+*.sol
+*.plc
+*.stc
+*.sts
+*.crc
+*.crs
+
+*.dri
+*.drl
+*.gpi
+*.pls
+
+*.drd
+*.drd.*
+
+*.info
+
+*.eps
+
+# file locks introduced since 7.x
+*.lck
+
diff --git a/vendor/gitignore/Elisp.gitignore b/vendor/gitignore/Elisp.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..9b4291b7fe84bd3c7ec2b4a483280a06f5bbebf5
--- /dev/null
+++ b/vendor/gitignore/Elisp.gitignore
@@ -0,0 +1,5 @@
+# Compiled
+*.elc
+
+# Packaging
+.cask
diff --git a/vendor/gitignore/Elixir.gitignore b/vendor/gitignore/Elixir.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..755b605549d85e74d41a649a0ce9bad1516bcf08
--- /dev/null
+++ b/vendor/gitignore/Elixir.gitignore
@@ -0,0 +1,5 @@
+/_build
+/cover
+/deps
+erl_crash.dump
+*.ez
diff --git a/vendor/gitignore/Elm.gitignore b/vendor/gitignore/Elm.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..a594364e2c02e00643c10a556d507a7cef4afe4a
--- /dev/null
+++ b/vendor/gitignore/Elm.gitignore
@@ -0,0 +1,4 @@
+# elm-package generated files
+elm-stuff/
+# elm-repl generated files
+repl-temp-*
diff --git a/vendor/gitignore/Erlang.gitignore b/vendor/gitignore/Erlang.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..8e46d5a07f8ffd5032ba920be8a26fe64b650535
--- /dev/null
+++ b/vendor/gitignore/Erlang.gitignore
@@ -0,0 +1,10 @@
+.eunit
+deps
+*.o
+*.beam
+*.plt
+erl_crash.dump
+ebin
+rel/example_project
+.concrete/DEV_MODE
+.rebar
diff --git a/vendor/gitignore/ExpressionEngine.gitignore b/vendor/gitignore/ExpressionEngine.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..314e4df123ac81790a9b8035c42f8d7e2d775794
--- /dev/null
+++ b/vendor/gitignore/ExpressionEngine.gitignore
@@ -0,0 +1,19 @@
+.DS_Store
+
+# Images
+images/avatars/
+images/captchas/
+images/smileys/
+images/member_photos/
+images/signature_attachments/
+images/pm_attachments/
+
+# For security do not publish the following files
+system/expressionengine/config/database.php
+system/expressionengine/config/config.php
+
+# Caches
+sized/
+thumbs/
+_thumbs/
+*/expressionengine/cache/*
diff --git a/vendor/gitignore/ExtJs.gitignore b/vendor/gitignore/ExtJs.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..5ffc21546ec4c944bbac2c25e6566b83dbb5cb76
--- /dev/null
+++ b/vendor/gitignore/ExtJs.gitignore
@@ -0,0 +1,4 @@
+.architect
+bootstrap.json
+build/
+ext/
diff --git a/vendor/gitignore/Fancy.gitignore b/vendor/gitignore/Fancy.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..70d6e631e55268e6bf2f051e37e4856c852a9d1e
--- /dev/null
+++ b/vendor/gitignore/Fancy.gitignore
@@ -0,0 +1,2 @@
+*.rbc
+*.fyc
diff --git a/vendor/gitignore/Finale.gitignore b/vendor/gitignore/Finale.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..7ef08e0c343f2ca3252bd3f78381a9920d70bdd8
--- /dev/null
+++ b/vendor/gitignore/Finale.gitignore
@@ -0,0 +1,13 @@
+*.bak
+*.db
+*.avi
+*.pdf
+*.ps
+*.mid
+*.midi
+*.mp3
+*.aif
+*.wav
+# Some versions of Finale have a bug and randomly save extra copies of
+# the music source as "<Filename> copy.mus"
+*copy.mus
diff --git a/vendor/gitignore/ForceDotCom.gitignore b/vendor/gitignore/ForceDotCom.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..3933cd4dd502f8a47cd5ea9b36293a12348ce12c
--- /dev/null
+++ b/vendor/gitignore/ForceDotCom.gitignore
@@ -0,0 +1,4 @@
+.project
+.settings
+salesforce.schema
+Referenced Packages
diff --git a/vendor/gitignore/Fortran.gitignore b/vendor/gitignore/Fortran.gitignore
new file mode 120000
index 0000000000000000000000000000000000000000..5daba98a3e6c9988fc042b3e191dee32b0f0e4a7
--- /dev/null
+++ b/vendor/gitignore/Fortran.gitignore
@@ -0,0 +1 @@
+C++.gitignore
\ No newline at end of file
diff --git a/vendor/gitignore/FuelPHP.gitignore b/vendor/gitignore/FuelPHP.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..d69f71f433894b801b796323b64b46ad6116dec7
--- /dev/null
+++ b/vendor/gitignore/FuelPHP.gitignore
@@ -0,0 +1,21 @@
+# the composer package lock file and install directory
+# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
+# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
+# /composer.lock
+/fuel/vendor
+
+# the fuelphp document
+/docs/
+
+# you may install these packages with `oil package`.
+# http://fuelphp.com/docs/packages/oil/package.html
+# /fuel/packages/auth/
+# /fuel/packages/email/
+# /fuel/packages/oil/
+# /fuel/packages/orm/
+# /fuel/packages/parser/
+
+# dynamically generated files
+/fuel/app/logs/*/*/*
+/fuel/app/cache/*/*
+/fuel/app/config/crypt.php
diff --git a/vendor/gitignore/GWT.gitignore b/vendor/gitignore/GWT.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..07704e54bbcdf04c547bca931b13695865eb7c28
--- /dev/null
+++ b/vendor/gitignore/GWT.gitignore
@@ -0,0 +1,28 @@
+*.class
+
+# Package Files #
+*.jar
+*.war
+
+# gwt caches and compiled units #
+war/gwt_bree/
+gwt-unitCache/
+
+# boilerplate generated classes #
+.apt_generated/
+
+# more caches and things from deploy #
+war/WEB-INF/deploy/
+war/WEB-INF/classes/
+
+#compilation logs
+.gwt/
+
+#caching for already compiled files
+gwt-unitCache/
+
+#gwt junit compilation files
+www-test/
+
+#old GWT (1.5) created this dir
+.gwt-tmp/
diff --git a/vendor/gitignore/Gcov.gitignore b/vendor/gitignore/Gcov.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..a6451430e174707029c8fc97467899ed91a42a09
--- /dev/null
+++ b/vendor/gitignore/Gcov.gitignore
@@ -0,0 +1,5 @@
+# gcc coverage testing tool files
+
+*.gcno
+*.gcda
+*.gcov
diff --git a/vendor/gitignore/GitBook.gitignore b/vendor/gitignore/GitBook.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..4cb12d8db77a1f9f11e1f7923b6f6f5075e003cf
--- /dev/null
+++ b/vendor/gitignore/GitBook.gitignore
@@ -0,0 +1,16 @@
+# Node rules:
+## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+## Dependency directory
+## Commenting this out is preferred by some people, see
+## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git
+node_modules
+
+# Book build output
+_book
+
+# eBook build output
+*.epub
+*.mobi
+*.pdf
diff --git a/vendor/gitignore/Global/Anjuta.gitignore b/vendor/gitignore/Global/Anjuta.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..20dd42c53e6f0df8233fee457b664d443ee729f4
--- /dev/null
+++ b/vendor/gitignore/Global/Anjuta.gitignore
@@ -0,0 +1,3 @@
+# Local configuration folder and symbol database
+/.anjuta/
+/.anjuta_sym_db.db
diff --git a/vendor/gitignore/Global/Archives.gitignore b/vendor/gitignore/Global/Archives.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..e9eda68baf2e6d0f8aeccb005fc1f5e308cdfa0d
--- /dev/null
+++ b/vendor/gitignore/Global/Archives.gitignore
@@ -0,0 +1,27 @@
+# It's better to unpack these files and commit the raw source because
+# git has its own built in compression methods.
+*.7z
+*.jar
+*.rar
+*.zip
+*.gz
+*.bzip
+*.bz2
+*.xz
+*.lzma
+*.cab
+
+#packing-only formats
+*.iso
+*.tar
+
+#package management formats
+*.dmg
+*.xpi
+*.gem
+*.egg
+*.deb
+*.rpm
+*.msi
+*.msm
+*.msp
diff --git a/vendor/gitignore/Global/BricxCC.gitignore b/vendor/gitignore/Global/BricxCC.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..c1d16a46c98ac2f2f2a7433f167ba407d2093a4a
--- /dev/null
+++ b/vendor/gitignore/Global/BricxCC.gitignore
@@ -0,0 +1,4 @@
+# Bricx Command Center IDE
+# http://bricxcc.sourceforge.net
+*.bak
+*.sym
diff --git a/vendor/gitignore/Global/CVS.gitignore b/vendor/gitignore/Global/CVS.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..1695352e146af3830cb9cf37f79c813b539f497f
--- /dev/null
+++ b/vendor/gitignore/Global/CVS.gitignore
@@ -0,0 +1,4 @@
+/CVS/*
+**/CVS/*
+.cvsignore
+*/.cvsignore
diff --git a/vendor/gitignore/Global/Calabash.gitignore b/vendor/gitignore/Global/Calabash.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..8a75b329dcdb6ecfff00b8ac80ee9f405c6c1a38
--- /dev/null
+++ b/vendor/gitignore/Global/Calabash.gitignore
@@ -0,0 +1,10 @@
+# Calabash / Cucumber
+rerun/
+reports/
+screenshots/
+screenshot*.png
+test-servers/
+
+# bundler
+.bundle
+vendor
diff --git a/vendor/gitignore/Global/Cloud9.gitignore b/vendor/gitignore/Global/Cloud9.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..3f4384df508b4d9dc2aa8b8f3b3f56a529b75e5c
--- /dev/null
+++ b/vendor/gitignore/Global/Cloud9.gitignore
@@ -0,0 +1,3 @@
+# Cloud9 IDE - http://c9.io
+.c9revisions
+.c9
diff --git a/vendor/gitignore/Global/CodeKit.gitignore b/vendor/gitignore/Global/CodeKit.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..bd9e67fcca234943806efb09a3b6ea4cf9b77c2f
--- /dev/null
+++ b/vendor/gitignore/Global/CodeKit.gitignore
@@ -0,0 +1,3 @@
+# General CodeKit files to ignore
+config.codekit
+/min
diff --git a/vendor/gitignore/Global/DartEditor.gitignore b/vendor/gitignore/Global/DartEditor.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..948920b420e783cc163dfedf9b8b38f0aed729ad
--- /dev/null
+++ b/vendor/gitignore/Global/DartEditor.gitignore
@@ -0,0 +1,2 @@
+.project
+.buildlog
diff --git a/vendor/gitignore/Global/Dreamweaver.gitignore b/vendor/gitignore/Global/Dreamweaver.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..0621a3d53b5c06bd9fe66e336ac5c833f11eae81
--- /dev/null
+++ b/vendor/gitignore/Global/Dreamweaver.gitignore
@@ -0,0 +1,7 @@
+# DW Dreamweaver added files
+_notes
+_compareTemp
+configs/
+dwsync.xml
+dw_php_codehinting.config
+*.mno
diff --git a/vendor/gitignore/Global/Dropbox.gitignore b/vendor/gitignore/Global/Dropbox.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..40f4a469d25229a8e8d3aefd90f43babc7f4ef58
--- /dev/null
+++ b/vendor/gitignore/Global/Dropbox.gitignore
@@ -0,0 +1,4 @@
+# Dropbox settings and caches
+.dropbox
+.dropbox.attr
+.dropbox.cache
diff --git a/vendor/gitignore/Global/Eclipse.gitignore b/vendor/gitignore/Global/Eclipse.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..31c9fb31167aa514b6c897eddbc6603e477c32ff
--- /dev/null
+++ b/vendor/gitignore/Global/Eclipse.gitignore
@@ -0,0 +1,51 @@
+
+.metadata
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.settings/
+.loadpath
+.recommenders
+
+# Eclipse Core
+.project
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# PyDev specific (Python IDE for Eclipse)
+*.pydevproject
+
+# CDT-specific (C/C++ Development Tooling)
+.cproject
+
+# JDT-specific (Eclipse Java Development Tools)
+.classpath
+
+# Java annotation processor (APT)
+.factorypath
+
+# PDT-specific (PHP Development Tools)
+.buildpath
+
+# sbteclipse plugin
+.target
+
+# Tern plugin
+.tern-project
+
+# TeXlipse plugin
+.texlipse
+
+# STS (Spring Tool Suite)
+.springBeans
+
+# Code Recommenders
+.recommenders/
diff --git a/vendor/gitignore/Global/EiffelStudio.gitignore b/vendor/gitignore/Global/EiffelStudio.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..f41b4f70216d896c6e9420c3392de9a44f1ed97f
--- /dev/null
+++ b/vendor/gitignore/Global/EiffelStudio.gitignore
@@ -0,0 +1,2 @@
+# The compilation directory
+EIFGENs
diff --git a/vendor/gitignore/Global/Emacs.gitignore b/vendor/gitignore/Global/Emacs.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..0c96c9ad0601bb5abd2fe8a696dc09b6de378920
--- /dev/null
+++ b/vendor/gitignore/Global/Emacs.gitignore
@@ -0,0 +1,42 @@
+# -*- mode: gitignore; -*-
+*~
+\#*\#
+/.emacs.desktop
+/.emacs.desktop.lock
+*.elc
+auto-save-list
+tramp
+.\#*
+
+# Org-mode
+.org-id-locations
+*_archive
+
+# flymake-mode
+*_flymake.*
+
+# eshell files
+/eshell/history
+/eshell/lastdir
+
+# elpa packages
+/elpa/
+
+# reftex files
+*.rel
+
+# AUCTeX auto folder
+/auto/
+
+# cask packages
+.cask/
+dist/
+
+# Flycheck
+flycheck_*.el
+
+# server auth directory
+/server/
+
+# projectiles files
+.projectile
\ No newline at end of file
diff --git a/vendor/gitignore/Global/Ensime.gitignore b/vendor/gitignore/Global/Ensime.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..f2daebb9f4b575b53e3fa101cf880342fb41990e
--- /dev/null
+++ b/vendor/gitignore/Global/Ensime.gitignore
@@ -0,0 +1,4 @@
+# Ensime specific
+.ensime
+.ensime_cache/
+.ensime_lucene/
diff --git a/vendor/gitignore/Global/Espresso.gitignore b/vendor/gitignore/Global/Espresso.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..1234530b5b320e2abc6d55e8d9a8b5ab7f59e53a
--- /dev/null
+++ b/vendor/gitignore/Global/Espresso.gitignore
@@ -0,0 +1 @@
+*.esproj
diff --git a/vendor/gitignore/Global/FlexBuilder.gitignore b/vendor/gitignore/Global/FlexBuilder.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..bbbfb91d9ebd03f852c3478393fe82ec579339ae
--- /dev/null
+++ b/vendor/gitignore/Global/FlexBuilder.gitignore
@@ -0,0 +1,3 @@
+bin/
+bin-debug/
+bin-release/
diff --git a/vendor/gitignore/Global/GPG.gitignore b/vendor/gitignore/Global/GPG.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..7740a01538cdcb5534016b18f2075629342ed658
--- /dev/null
+++ b/vendor/gitignore/Global/GPG.gitignore
@@ -0,0 +1,2 @@
+secring.*
+
diff --git a/vendor/gitignore/Global/IPythonNotebook.gitignore b/vendor/gitignore/Global/IPythonNotebook.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..27c13510bf5ef442fd309edcb71bb48139fdebfb
--- /dev/null
+++ b/vendor/gitignore/Global/IPythonNotebook.gitignore
@@ -0,0 +1,2 @@
+# Temporary data
+.ipynb_checkpoints/
diff --git a/vendor/gitignore/Global/JDeveloper.gitignore b/vendor/gitignore/Global/JDeveloper.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..5bba6f377338c915fb10f6c50fc009d8458ab710
--- /dev/null
+++ b/vendor/gitignore/Global/JDeveloper.gitignore
@@ -0,0 +1,13 @@
+# default application storage directory used by the IDE Performance Cache feature
+.data/
+
+# used for ADF styles caching
+temp/
+
+# default output directories
+classes/
+deploy/
+javadoc/
+
+# lock file, a part of Oracle Credential Store Framework
+cwallet.sso.lck
\ No newline at end of file
diff --git a/vendor/gitignore/Global/JetBrains.gitignore b/vendor/gitignore/Global/JetBrains.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..ea83a5eb620684a6c672e78fd2f0fe66d3410fd9
--- /dev/null
+++ b/vendor/gitignore/Global/JetBrains.gitignore
@@ -0,0 +1,44 @@
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff:
+.idea/workspace.xml
+.idea/tasks.xml
+.idea/dictionaries
+.idea/vcs.xml
+.idea/jsLibraryMappings.xml
+
+# Sensitive or high-churn files:
+.idea/dataSources.ids
+.idea/dataSources.xml
+.idea/dataSources.local.xml
+.idea/sqlDataSources.xml
+.idea/dynamic.xml
+.idea/uiDesigner.xml
+
+# Gradle:
+.idea/gradle.xml
+.idea/libraries
+
+# Mongo Explorer plugin:
+.idea/mongoSettings.xml
+
+## File-based project format:
+*.iws
+
+## Plugin-specific files:
+
+# IntelliJ
+/out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
diff --git a/vendor/gitignore/Global/KDevelop4.gitignore b/vendor/gitignore/Global/KDevelop4.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..7ac57b1add40a1974f992dd2c4cab849daaaf345
--- /dev/null
+++ b/vendor/gitignore/Global/KDevelop4.gitignore
@@ -0,0 +1,2 @@
+*.kdev4
+.kdev4/
diff --git a/vendor/gitignore/Global/Kate.gitignore b/vendor/gitignore/Global/Kate.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..7ff06ce539036131fdd5e0e194cb8890411f4333
--- /dev/null
+++ b/vendor/gitignore/Global/Kate.gitignore
@@ -0,0 +1,3 @@
+# Swap Files #
+.*.kate-swp
+.swp.*
diff --git a/vendor/gitignore/Global/Lazarus.gitignore b/vendor/gitignore/Global/Lazarus.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b32943f1c6e718ae2f8ff201a0c3f3c10d4cfb84
--- /dev/null
+++ b/vendor/gitignore/Global/Lazarus.gitignore
@@ -0,0 +1,30 @@
+# Lazarus compiler-generated binaries (safe to delete)
+*.exe
+*.dll
+*.so
+*.dylib
+*.lrs
+*.res
+*.compiled
+*.dbg
+*.ppu
+*.o
+*.or
+*.a
+
+# Lazarus autogenerated files (duplicated info)
+*.rst
+*.rsj
+*.lrt
+
+# Lazarus local files (user-specific info)
+*.lps
+
+# Lazarus backups and unit output folders.
+# These can be changed by user in Lazarus/project options.
+backup/
+*.bak
+lib/
+
+# Application bundle for Mac OS
+*.app/
diff --git a/vendor/gitignore/Global/LibreOffice.gitignore b/vendor/gitignore/Global/LibreOffice.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..586beac91d3c9a6e85cef5706aa8c49c301587f1
--- /dev/null
+++ b/vendor/gitignore/Global/LibreOffice.gitignore
@@ -0,0 +1,2 @@
+# LibreOffice locks
+.~lock.*#
diff --git a/vendor/gitignore/Global/Linux.gitignore b/vendor/gitignore/Global/Linux.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..cc9586893b6ed581866f0ccbea4584b6abd59a7a
--- /dev/null
+++ b/vendor/gitignore/Global/Linux.gitignore
@@ -0,0 +1,10 @@
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
diff --git a/vendor/gitignore/Global/LyX.gitignore b/vendor/gitignore/Global/LyX.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..8efe0195cf363a07c69f74da3f28e762933d7a63
--- /dev/null
+++ b/vendor/gitignore/Global/LyX.gitignore
@@ -0,0 +1,4 @@
+# Ignore LyX backup and autosave files
+# http://www.lyx.org/
+*.lyx~
+*.lyx#
diff --git a/vendor/gitignore/Global/Matlab.gitignore b/vendor/gitignore/Global/Matlab.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..32a5ad4c7778d94ff20452371834cba8b7ebeda5
--- /dev/null
+++ b/vendor/gitignore/Global/Matlab.gitignore
@@ -0,0 +1,19 @@
+##---------------------------------------------------
+## Remove autosaves generated by the Matlab editor
+## We have git for backups!
+##---------------------------------------------------
+
+# Windows default autosave extension
+*.asv
+
+# OSX / *nix default autosave extension
+*.m~
+
+# Compiled MEX binaries (all platforms)
+*.mex*
+
+# Simulink Code Generation
+slprj/
+
+# Session info
+octave-workspace
diff --git a/vendor/gitignore/Global/Mercurial.gitignore b/vendor/gitignore/Global/Mercurial.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..e65d113798823a541056bda2495e83046b680cf3
--- /dev/null
+++ b/vendor/gitignore/Global/Mercurial.gitignore
@@ -0,0 +1,6 @@
+.hg/
+.hgignore
+.hgsigs
+.hgsub
+.hgsubstate
+.hgtags
diff --git a/vendor/gitignore/Global/MicrosoftOffice.gitignore b/vendor/gitignore/Global/MicrosoftOffice.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..cb89174566014e1f19b817659251738dff88999d
--- /dev/null
+++ b/vendor/gitignore/Global/MicrosoftOffice.gitignore
@@ -0,0 +1,16 @@
+*.tmp
+
+# Word temporary
+~$*.doc*
+
+# Excel temporary
+~$*.xls*
+
+# Excel Backup File
+*.xlk
+
+# PowerPoint temporary
+~$*.ppt*
+
+# Visio autosave temporary files
+*.~vsdx
diff --git a/vendor/gitignore/Global/ModelSim.gitignore b/vendor/gitignore/Global/ModelSim.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..46592b864309fb7f92a1d01157127e69545cfb4c
--- /dev/null
+++ b/vendor/gitignore/Global/ModelSim.gitignore
@@ -0,0 +1,23 @@
+# ignore ModelSim generated files and directories (temp files and so on)
+[_@]*
+
+# ignore compilation output of ModelSim
+*.mti
+*.dat
+*.dbs
+*.psm
+*.bak
+*.cmp
+*.jpg
+*.html
+*.bsf
+
+# ignore simulation output of ModelSim
+wlf*
+*.wlf
+*.vstf
+*.ucdb
+cov*/
+transcript*
+sc_dpiheader.h
+vsim.dbg
diff --git a/vendor/gitignore/Global/Momentics.gitignore b/vendor/gitignore/Global/Momentics.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b14db2d8645e862c5d804f2ed58f51d2b6f6392e
--- /dev/null
+++ b/vendor/gitignore/Global/Momentics.gitignore
@@ -0,0 +1,8 @@
+# Built files
+x86/
+arm/
+arm-p/
+translations/*.qm
+
+# IDE settings
+.settings/
diff --git a/vendor/gitignore/Global/MonoDevelop.gitignore b/vendor/gitignore/Global/MonoDevelop.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..ef38d06b08f1a71d7e38804ceae506370917f063
--- /dev/null
+++ b/vendor/gitignore/Global/MonoDevelop.gitignore
@@ -0,0 +1,8 @@
+#User Specific
+*.userprefs
+*.usertasks
+
+#Mono Project Files
+*.pidb
+*.resources
+test-results/
diff --git a/vendor/gitignore/Global/NetBeans.gitignore b/vendor/gitignore/Global/NetBeans.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..520d91ff584bda8a9385fb02acbbf03e0d35fed3
--- /dev/null
+++ b/vendor/gitignore/Global/NetBeans.gitignore
@@ -0,0 +1,7 @@
+nbproject/private/
+build/
+nbbuild/
+dist/
+nbdist/
+nbactions.xml
+.nb-gradle/
diff --git a/vendor/gitignore/Global/Ninja.gitignore b/vendor/gitignore/Global/Ninja.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..50e58f24cc9b2f2df1930192503ead036e6fc764
--- /dev/null
+++ b/vendor/gitignore/Global/Ninja.gitignore
@@ -0,0 +1,2 @@
+.ninja_deps
+.ninja_log
diff --git a/vendor/gitignore/Global/NotepadPP.gitignore b/vendor/gitignore/Global/NotepadPP.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..8fbda83a2c96d96fc56d5913852bd7d360830b58
--- /dev/null
+++ b/vendor/gitignore/Global/NotepadPP.gitignore
@@ -0,0 +1,2 @@
+# Notepad++ backups #
+*.bak
diff --git a/vendor/gitignore/Global/OSX.gitignore b/vendor/gitignore/Global/OSX.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..660b31353e8082941ae527149c107e4c26ea1e2a
--- /dev/null
+++ b/vendor/gitignore/Global/OSX.gitignore
@@ -0,0 +1,24 @@
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon

+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
diff --git a/vendor/gitignore/Global/Otto.gitignore b/vendor/gitignore/Global/Otto.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..5aa263f9db03327b7a58a134f3a0005c280644af
--- /dev/null
+++ b/vendor/gitignore/Global/Otto.gitignore
@@ -0,0 +1 @@
+.otto/
diff --git a/vendor/gitignore/Global/Redcar.gitignore b/vendor/gitignore/Global/Redcar.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b4a9d1d68e3b1dcaace9308b0562b55e992ebc26
--- /dev/null
+++ b/vendor/gitignore/Global/Redcar.gitignore
@@ -0,0 +1 @@
+.redcar
diff --git a/vendor/gitignore/Global/Redis.gitignore b/vendor/gitignore/Global/Redis.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..57c1c230f920f9872a433c20b56af2adc99de996
--- /dev/null
+++ b/vendor/gitignore/Global/Redis.gitignore
@@ -0,0 +1,3 @@
+# Ignore redis binary dump (dump.rdb) files
+
+*.rdb
diff --git a/vendor/gitignore/Global/SBT.gitignore b/vendor/gitignore/Global/SBT.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..970d897c75cee1d88fb6e021e8819a9bb446cd99
--- /dev/null
+++ b/vendor/gitignore/Global/SBT.gitignore
@@ -0,0 +1,9 @@
+# Simple Build Tool
+# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control
+
+target/
+lib_managed/
+src_managed/
+project/boot/
+.history
+.cache
diff --git a/vendor/gitignore/Global/SVN.gitignore b/vendor/gitignore/Global/SVN.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..1b53ace613fe442baa4dcbad46184351572d61f9
--- /dev/null
+++ b/vendor/gitignore/Global/SVN.gitignore
@@ -0,0 +1 @@
+.svn/
diff --git a/vendor/gitignore/Global/SlickEdit.gitignore b/vendor/gitignore/Global/SlickEdit.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..f30b8da457c6da2d2fc9641dd9f962e57b7fb13c
--- /dev/null
+++ b/vendor/gitignore/Global/SlickEdit.gitignore
@@ -0,0 +1,11 @@
+# SlickEdit workspace and project files are ignored by default because
+# typically they are considered to be developer-specific and not part of a
+# project.
+*.vpw
+*.vpj
+
+# SlickEdit workspace history and tag files always contain user-specific
+# data so they should not be stored in a repository.
+*.vpwhistu
+*.vpwhist
+*.vtg
diff --git a/vendor/gitignore/Global/SublimeText.gitignore b/vendor/gitignore/Global/SublimeText.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..1d4e613759162e4307104d039181ff6e2c3650f4
--- /dev/null
+++ b/vendor/gitignore/Global/SublimeText.gitignore
@@ -0,0 +1,14 @@
+# cache files for sublime text
+*.tmlanguage.cache
+*.tmPreferences.cache
+*.stTheme.cache
+
+# workspace files are user-specific
+*.sublime-workspace
+
+# project files should be checked into the repository, unless a significant
+# proportion of contributors will probably not be using SublimeText
+# *.sublime-project
+
+# sftp configuration file
+sftp-config.json
diff --git a/vendor/gitignore/Global/SynopsysVCS.gitignore b/vendor/gitignore/Global/SynopsysVCS.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..eed2432fb787a27d0b58416a1357b9fca6e39c75
--- /dev/null
+++ b/vendor/gitignore/Global/SynopsysVCS.gitignore
@@ -0,0 +1,36 @@
+# Waveform formats
+*.vcd
+*.vpd
+*.evcd
+*.fsdb
+
+# Default name of the simulation executable.  A different name can be 
+# specified with this switch (the associated daidir database name is 
+# also taken from here):  -o <path>/<filename>
+simv
+
+# Generated for Verilog and VHDL top configs
+simv.daidir/
+simv.db.dir/
+
+# Infrastructure necessary to co-simulate SystemC models with 
+# Verilog/VHDL models.  An alternate directory may be specified with this
+# switch:  -Mdir=<directory_path>
+csrc/
+
+# Log file - the following switch allows to specify the file that will be
+# used to write all messages from simulation:  -l <filename>
+*.log
+
+# Coverage results (generated with urg) and database location.  The 
+# following switch can also be used:  urg -dir <coverage_directory>.vdb
+simv.vdb/
+urgReport/
+
+# DVE and UCLI related files.
+DVEfiles/
+ucli.key
+
+# When the design is elaborated for DirectC, the following file is created
+# with declarations for C/C++ functions.
+vc_hdrs.h
diff --git a/vendor/gitignore/Global/Tags.gitignore b/vendor/gitignore/Global/Tags.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..c0318165a2790cedcc8a27765b3e8bc2f586006c
--- /dev/null
+++ b/vendor/gitignore/Global/Tags.gitignore
@@ -0,0 +1,16 @@
+# Ignore tags created by etags, ctags, gtags (GNU global) and cscope
+TAGS
+.TAGS
+!TAGS/
+tags
+.tags
+!tags/
+gtags.files
+GTAGS
+GRTAGS
+GPATH
+cscope.files
+cscope.out
+cscope.in.out
+cscope.po.out
+
diff --git a/vendor/gitignore/Global/TextMate.gitignore b/vendor/gitignore/Global/TextMate.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..41e8d07a940af8caeb427cabea4c28e3c4b17480
--- /dev/null
+++ b/vendor/gitignore/Global/TextMate.gitignore
@@ -0,0 +1,3 @@
+*.tmproj
+*.tmproject
+tmtags
diff --git a/vendor/gitignore/Global/TortoiseGit.gitignore b/vendor/gitignore/Global/TortoiseGit.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..db89590a6297e3e9675131467085967b436719ed
--- /dev/null
+++ b/vendor/gitignore/Global/TortoiseGit.gitignore
@@ -0,0 +1,2 @@
+# Project-level settings
+/.tgitconfig
diff --git a/vendor/gitignore/Global/Vagrant.gitignore b/vendor/gitignore/Global/Vagrant.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..a977916f6583710870b00d50dd7fddd6701ece11
--- /dev/null
+++ b/vendor/gitignore/Global/Vagrant.gitignore
@@ -0,0 +1 @@
+.vagrant/
diff --git a/vendor/gitignore/Global/Vim.gitignore b/vendor/gitignore/Global/Vim.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..bdc04a0b529b192db15e4a7a98450d94e5c5501d
--- /dev/null
+++ b/vendor/gitignore/Global/Vim.gitignore
@@ -0,0 +1,10 @@
+# swap
+[._]*.s[a-w][a-z]
+[._]s[a-w][a-z]
+# session
+Session.vim
+# temporary
+.netrwhist
+*~
+# auto-generated tag files
+tags
diff --git a/vendor/gitignore/Global/VirtualEnv.gitignore b/vendor/gitignore/Global/VirtualEnv.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b2c22f2af7f40864033841b32c572de4c60eff1d
--- /dev/null
+++ b/vendor/gitignore/Global/VirtualEnv.gitignore
@@ -0,0 +1,12 @@
+# Virtualenv
+# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
+.Python
+[Bb]in
+[Ii]nclude
+[Ll]ib
+[Ll]ib64
+[Ll]ocal
+[Ss]cripts
+pyvenv.cfg
+.venv
+pip-selfcheck.json
diff --git a/vendor/gitignore/Global/VisualStudioCode.gitignore b/vendor/gitignore/Global/VisualStudioCode.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..faa18382a3c9eeeea55949156e562c33220f7148
--- /dev/null
+++ b/vendor/gitignore/Global/VisualStudioCode.gitignore
@@ -0,0 +1,2 @@
+.vscode
+
diff --git a/vendor/gitignore/Global/WebMethods.gitignore b/vendor/gitignore/Global/WebMethods.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b383c25ca3c7421b63e282e466957e25697a6f92
--- /dev/null
+++ b/vendor/gitignore/Global/WebMethods.gitignore
@@ -0,0 +1,14 @@
+**/IntegrationServer/datastore/
+**/IntegrationServer/db/
+**/IntegrationServer/DocumentStore/
+**/IntegrationServer/lib/
+**/IntegrationServer/logs/
+**/IntegrationServer/replicate/
+**/IntegrationServer/sdk/
+**/IntegrationServer/support/
+**/IntegrationServer/update/
+**/IntegrationServer/userFtpRoot/
+**/IntegrationServer/web/
+**/IntegrationServer/WmRepository4/
+**/IntegrationServer/XAStore/
+**/IntegrationServer/packages/Wm*/
diff --git a/vendor/gitignore/Global/Windows.gitignore b/vendor/gitignore/Global/Windows.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..a0d31452b0e29226c83da01d538f29dc8f338e73
--- /dev/null
+++ b/vendor/gitignore/Global/Windows.gitignore
@@ -0,0 +1,18 @@
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
diff --git a/vendor/gitignore/Global/Xcode.gitignore b/vendor/gitignore/Global/Xcode.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..37de8bb4793f174d4cb4dab98b2ead9020b86d21
--- /dev/null
+++ b/vendor/gitignore/Global/Xcode.gitignore
@@ -0,0 +1,23 @@
+# Xcode
+#
+# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
+
+## Build generated
+build/
+DerivedData/
+
+## Various settings
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata/
+
+## Other
+*.moved-aside
+*.xccheckout
+*.xcscmblueprint
diff --git a/vendor/gitignore/Global/XilinxISE.gitignore b/vendor/gitignore/Global/XilinxISE.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..4475f843da99d685d87c68a7c4ea5d4e00fa6563
--- /dev/null
+++ b/vendor/gitignore/Global/XilinxISE.gitignore
@@ -0,0 +1,67 @@
+# intermediate build files
+*.bgn
+*.bit
+*.bld
+*.cmd_log
+*.drc
+*.ll
+*.lso
+*.msd
+*.msk
+*.ncd
+*.ngc
+*.ngd
+*.ngr
+*.pad
+*.par
+*.pcf
+*.prj
+*.ptwx
+*.rbb
+*.rbd
+*.stx
+*.syr
+*.twr
+*.twx
+*.unroutes
+*.ut
+*.xpi
+*.xst
+*_bitgen.xwbt
+*_envsettings.html
+*_map.map
+*_map.mrp
+*_map.ngm
+*_map.xrpt
+*_ngdbuild.xrpt
+*_pad.csv
+*_pad.txt
+*_par.xrpt
+*_summary.html
+*_summary.xml
+*_usage.xml
+*_xst.xrpt
+
+# iMPACT generated files
+_impactbatch.log
+impact.xsl
+impact_impact.xwbt
+ise_impact.cmd
+webtalk_impact.xml
+
+# Core Generator generated files
+xaw2verilog.log
+
+# project-wide generated files
+*.gise
+par_usage_statistics.html
+usage_statistics_webtalk.html
+webtalk.log
+webtalk_pn.xml
+
+# generated folders
+iseconfig/
+xlnx_auto_0_xdb/
+xst/
+_ngo/
+_xmsgs/
diff --git a/vendor/gitignore/Go.gitignore b/vendor/gitignore/Go.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..daf913b1b347aae6de6f48d599bc89ef8c8693d6
--- /dev/null
+++ b/vendor/gitignore/Go.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/vendor/gitignore/Gradle.gitignore b/vendor/gitignore/Gradle.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..77617a15c3818f6a8d59f26307467a62409f9751
--- /dev/null
+++ b/vendor/gitignore/Gradle.gitignore
@@ -0,0 +1,14 @@
+.gradle
+build/
+
+# Ignore Gradle GUI config
+gradle-app.setting
+
+# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
+!gradle-wrapper.jar
+
+# Cache of project
+.gradletasknamecache
+
+# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
+# gradle/wrapper/gradle-wrapper.properties
diff --git a/vendor/gitignore/Grails.gitignore b/vendor/gitignore/Grails.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..9185f14c37cea61288692c406f086577750b8ec5
--- /dev/null
+++ b/vendor/gitignore/Grails.gitignore
@@ -0,0 +1,33 @@
+# .gitignore for Grails 1.2 and 1.3
+# Although this should work for most versions of grails, it is
+# suggested that you use the "grails integrate-with --git" command
+# to generate your .gitignore file.
+
+# web application files
+/web-app/WEB-INF/classes
+
+# default HSQL database files for production mode
+/prodDb.*
+
+# general HSQL database files
+*Db.properties
+*Db.script
+
+# logs
+/stacktrace.log
+/test/reports
+/logs
+
+# project release file
+/*.war
+
+# plugin release files
+/*.zip
+/plugin.xml
+
+# older plugin install locations
+/plugins
+/web-app/plugins
+
+# "temporary" build files
+/target
diff --git a/vendor/gitignore/Haskell.gitignore b/vendor/gitignore/Haskell.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..096abdd90b3be1288ae47044098d3bd981635469
--- /dev/null
+++ b/vendor/gitignore/Haskell.gitignore
@@ -0,0 +1,18 @@
+dist
+dist-*
+cabal-dev
+*.o
+*.hi
+*.chi
+*.chs.h
+*.dyn_o
+*.dyn_hi
+.hpc
+.hsenv
+.cabal-sandbox/
+cabal.sandbox.config
+*.prof
+*.aux
+*.hp
+*.eventlog
+.stack-work/
diff --git a/vendor/gitignore/IGORPro.gitignore b/vendor/gitignore/IGORPro.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..c62be65003661fa515e1301b03e82ecac7a59a94
--- /dev/null
+++ b/vendor/gitignore/IGORPro.gitignore
@@ -0,0 +1,5 @@
+# Avoid including Experiment files: they can be created and edited locally to test the ipf files
+*.pxp
+*.pxt
+*.uxp
+*.uxt
diff --git a/vendor/gitignore/Idris.gitignore b/vendor/gitignore/Idris.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..c28bc7cc675f54a316a8944d22674529b9d21210
--- /dev/null
+++ b/vendor/gitignore/Idris.gitignore
@@ -0,0 +1,2 @@
+*.ibc
+*.o
diff --git a/vendor/gitignore/Java.gitignore b/vendor/gitignore/Java.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..32858aad3c383ed1ff0a0f9bdf231d54a00c9e88
--- /dev/null
+++ b/vendor/gitignore/Java.gitignore
@@ -0,0 +1,12 @@
+*.class
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
diff --git a/vendor/gitignore/Jboss.gitignore b/vendor/gitignore/Jboss.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..75d1731ed97a0077c65f77aa8c73ca4bdd9940e1
--- /dev/null
+++ b/vendor/gitignore/Jboss.gitignore
@@ -0,0 +1,19 @@
+jboss/server/all/deploy/project.ext
+jboss/server/default/deploy/project.ext
+jboss/server/minimal/deploy/project.ext
+jboss/server/all/log/*.log
+jboss/server/all/tmp/**/*
+jboss/server/all/data/**/*
+jboss/server/all/work/**/*
+jboss/server/default/log/*.log
+jboss/server/default/tmp/**/*
+jboss/server/default/data/**/*
+jboss/server/default/work/**/*
+jboss/server/minimal/log/*.log
+jboss/server/minimal/tmp/**/*
+jboss/server/minimal/data/**/*
+jboss/server/minimal/work/**/*
+
+# deployed package files #
+
+*.DEPLOYED
diff --git a/vendor/gitignore/Jekyll.gitignore b/vendor/gitignore/Jekyll.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..5c91b60c063e0bdb3819309e304804db380b9748
--- /dev/null
+++ b/vendor/gitignore/Jekyll.gitignore
@@ -0,0 +1,3 @@
+_site/
+.sass-cache/
+.jekyll-metadata
diff --git a/vendor/gitignore/Joomla.gitignore b/vendor/gitignore/Joomla.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..0d7a0de298faec7abe9c2592558364fbdda7bde7
--- /dev/null
+++ b/vendor/gitignore/Joomla.gitignore
@@ -0,0 +1,546 @@
+/.gitignore
+/.htaccess
+/administrator/cache/*
+/administrator/components/com_admin/*
+/administrator/components/com_ajax/*
+/administrator/components/com_tags/*
+/administrator/components/com_banners/*
+/administrator/components/com_cache/*
+/administrator/components/com_postinstall/*
+/administrator/components/com_joomlaupdate/*
+/administrator/components/com_contenthistory/*
+/administrator/components/com_categories/*
+/administrator/components/com_checkin/*
+/administrator/components/com_config/*
+/administrator/components/com_contact/*
+/administrator/components/com_content/*
+/administrator/components/com_cpanel/*
+/administrator/components/com_finder/*
+/administrator/components/com_installer/*
+/administrator/components/com_languages/*
+/administrator/components/com_login/*
+/administrator/components/com_media/*
+/administrator/components/com_menus/*
+/administrator/components/com_messages/*
+/administrator/components/com_modules/*
+/administrator/components/com_newsfeeds/*
+/administrator/components/com_plugins/*
+/administrator/components/com_redirect/*
+/administrator/components/com_search/*
+/administrator/components/com_templates/*
+/administrator/components/com_users/*
+/administrator/components/com_weblinks/*
+/administrator/components/index.html
+/administrator/help/*
+/administrator/includes/*
+/administrator/language/en-GB/en-GB.com_ajax.ini
+/administrator/language/en-GB/en-GB.com_ajax.sys.ini
+/administrator/language/en-GB/en-GB.com_contenthistory.ini
+/administrator/language/en-GB/en-GB.com_contenthistory.sys.ini
+/administrator/language/en-GB/en-GB.com_joomlaupdate.ini
+/administrator/language/en-GB/en-GB.com_joomlaupdate.sys.ini
+/administrator/language/en-GB/en-GB.com_postinstall.ini
+/administrator/language/en-GB/en-GB.com_postinstall.sys.ini
+/administrator/language/en-GB/en-GB.com_sitemapjen.sys.ini
+/administrator/language/en-GB/en-GB.com_tags.ini
+/administrator/language/en-GB/en-GB.com_tags.sys.ini
+/administrator/language/en-GB/en-GB.mod_stats_admin.ini
+/administrator/language/en-GB/en-GB.mod_stats_admin.sys.ini
+/administrator/language/en-GB/en-GB.plg_authentication_cookie.ini
+/administrator/language/en-GB/en-GB.plg_authentication_cookie.sys.ini
+/administrator/language/en-GB/en-GB.plg_content_contact.ini
+/administrator/language/en-GB/en-GB.plg_content_contact.sys.ini
+/administrator/language/en-GB/en-GB.plg_content_finder.ini
+/administrator/language/en-GB/en-GB.plg_content_finder.sys.ini
+/administrator/language/en-GB/en-GB.plg_finder_categories.ini
+/administrator/language/en-GB/en-GB.plg_finder_categories.sys.ini
+/administrator/language/en-GB/en-GB.plg_finder_contacts.ini
+/administrator/language/en-GB/en-GB.plg_finder_contacts.sys.ini
+/administrator/language/en-GB/en-GB.plg_finder_content.ini
+/administrator/language/en-GB/en-GB.plg_finder_content.sys.ini
+/administrator/language/en-GB/en-GB.plg_finder_newsfeeds.sys.ini
+/administrator/language/en-GB/en-GB.plg_finder_newsfeeds.ini
+/administrator/language/en-GB/en-GB.plg_finder_tags.ini
+/administrator/language/en-GB/en-GB.plg_finder_tags.sys.ini
+/administrator/language/en-GB/en-GB.plg_finder_weblinks.ini
+/administrator/language/en-GB/en-GB.plg_finder_weblinks.sys.ini
+/administrator/language/en-GB/en-GB.plg_installer_webinstaller.ini
+/administrator/language/en-GB/en-GB.plg_installer_webinstaller.sys.ini
+/administrator/language/en-GB/en-GB.plg_quickicon_joomlaupdate.ini
+/administrator/language/en-GB/en-GB.plg_quickicon_joomlaupdate.sys.ini
+/administrator/language/en-GB/en-GB.plg_search_tags.ini
+/administrator/language/en-GB/en-GB.plg_search_tags.sys.ini
+/administrator/language/en-GB/en-GB.plg_system_languagecode.ini
+/administrator/language/en-GB/en-GB.plg_system_languagecode.sys.ini
+/administrator/language/en-GB/en-GB.plg_twofactorauth_totp.ini
+/administrator/language/en-GB/en-GB.plg_twofactorauth_totp.sys.ini
+/administrator/language/en-GB/en-GB.plg_twofactorauth_yubikey.ini
+/administrator/language/en-GB/en-GB.plg_twofactorauth_yubikey.sys.ini
+/administrator/language/en-GB/en-GB.tpl_isis.ini
+/administrator/language/en-GB/en-GB.tpl_isis.sys.ini
+/administrator/language/en-GB/install.xml
+/administrator/language/en-GB/en-GB.com_admin.ini
+/administrator/language/en-GB/en-GB.com_admin.sys.ini
+/administrator/language/en-GB/en-GB.com_banners.ini
+/administrator/language/en-GB/en-GB.com_banners.sys.ini
+/administrator/language/en-GB/en-GB.com_cache.ini
+/administrator/language/en-GB/en-GB.com_cache.sys.ini
+/administrator/language/en-GB/en-GB.com_categories.ini
+/administrator/language/en-GB/en-GB.com_categories.sys.ini
+/administrator/language/en-GB/en-GB.com_checkin.ini
+/administrator/language/en-GB/en-GB.com_checkin.sys.ini
+/administrator/language/en-GB/en-GB.com_config.ini
+/administrator/language/en-GB/en-GB.com_config.sys.ini
+/administrator/language/en-GB/en-GB.com_contact.ini
+/administrator/language/en-GB/en-GB.com_contact.sys.ini
+/administrator/language/en-GB/en-GB.com_content.ini
+/administrator/language/en-GB/en-GB.com_content.sys.ini
+/administrator/language/en-GB/en-GB.com_cpanel.ini
+/administrator/language/en-GB/en-GB.com_cpanel.sys.ini
+/administrator/language/en-GB/en-GB.com_finder.ini
+/administrator/language/en-GB/en-GB.com_finder.sys.ini
+/administrator/language/en-GB/en-GB.com_installer.ini
+/administrator/language/en-GB/en-GB.com_installer.sys.ini
+/administrator/language/en-GB/en-GB.com_languages.ini
+/administrator/language/en-GB/en-GB.com_languages.sys.ini
+/administrator/language/en-GB/en-GB.com_login.ini
+/administrator/language/en-GB/en-GB.com_login.sys.ini
+/administrator/language/en-GB/en-GB.com_mailto.sys.ini
+/administrator/language/en-GB/en-GB.com_media.ini
+/administrator/language/en-GB/en-GB.com_media.sys.ini
+/administrator/language/en-GB/en-GB.com_menus.ini
+/administrator/language/en-GB/en-GB.com_menus.sys.ini
+/administrator/language/en-GB/en-GB.com_messages.ini
+/administrator/language/en-GB/en-GB.com_messages.sys.ini
+/administrator/language/en-GB/en-GB.com_modules.ini
+/administrator/language/en-GB/en-GB.com_modules.sys.ini
+/administrator/language/en-GB/en-GB.com_newsfeeds.ini
+/administrator/language/en-GB/en-GB.com_newsfeeds.sys.ini
+/administrator/language/en-GB/en-GB.com_plugins.ini
+/administrator/language/en-GB/en-GB.com_plugins.sys.ini
+/administrator/language/en-GB/en-GB.com_redirect.ini
+/administrator/language/en-GB/en-GB.com_redirect.sys.ini
+/administrator/language/en-GB/en-GB.com_search.ini
+/administrator/language/en-GB/en-GB.com_search.sys.ini
+/administrator/language/en-GB/en-GB.com_templates.ini
+/administrator/language/en-GB/en-GB.com_templates.sys.ini
+/administrator/language/en-GB/en-GB.com_users.ini
+/administrator/language/en-GB/en-GB.com_users.sys.ini
+/administrator/language/en-GB/en-GB.com_weblinks.ini
+/administrator/language/en-GB/en-GB.com_weblinks.sys.ini
+/administrator/language/en-GB/en-GB.com_wrapper.ini
+/administrator/language/en-GB/en-GB.com_wrapper.sys.ini
+/administrator/language/en-GB/en-GB.ini
+/administrator/language/en-GB/en-GB.lib_joomla.ini
+/administrator/language/en-GB/en-GB.localise.php
+/administrator/language/en-GB/en-GB.mod_custom.ini
+/administrator/language/en-GB/en-GB.mod_custom.sys.ini
+/administrator/language/en-GB/en-GB.mod_feed.ini
+/administrator/language/en-GB/en-GB.mod_feed.sys.ini
+/administrator/language/en-GB/en-GB.mod_latest.ini
+/administrator/language/en-GB/en-GB.mod_latest.sys.ini
+/administrator/language/en-GB/en-GB.mod_logged.ini
+/administrator/language/en-GB/en-GB.mod_logged.sys.ini
+/administrator/language/en-GB/en-GB.mod_login.ini
+/administrator/language/en-GB/en-GB.mod_login.sys.ini
+/administrator/language/en-GB/en-GB.mod_menu.ini
+/administrator/language/en-GB/en-GB.mod_menu.sys.ini
+/administrator/language/en-GB/en-GB.mod_multilangstatus.ini
+/administrator/language/en-GB/en-GB.mod_multilangstatus.sys.ini
+/administrator/language/en-GB/en-GB.mod_online.ini
+/administrator/language/en-GB/en-GB.mod_online.sys.ini
+/administrator/language/en-GB/en-GB.mod_popular.ini
+/administrator/language/en-GB/en-GB.mod_popular.sys.ini
+/administrator/language/en-GB/en-GB.mod_quickicon.ini
+/administrator/language/en-GB/en-GB.mod_quickicon.sys.ini
+/administrator/language/en-GB/en-GB.mod_status.ini
+/administrator/language/en-GB/en-GB.mod_status.sys.ini
+/administrator/language/en-GB/en-GB.mod_submenu.ini
+/administrator/language/en-GB/en-GB.mod_submenu.sys.ini
+/administrator/language/en-GB/en-GB.mod_title.ini
+/administrator/language/en-GB/en-GB.mod_title.sys.ini
+/administrator/language/en-GB/en-GB.mod_toolbar.ini
+/administrator/language/en-GB/en-GB.mod_toolbar.sys.ini
+/administrator/language/en-GB/en-GB.mod_unread.ini
+/administrator/language/en-GB/en-GB.mod_unread.sys.ini
+/administrator/language/en-GB/en-GB.mod_version.ini
+/administrator/language/en-GB/en-GB.mod_version.sys.ini
+/administrator/language/en-GB/en-GB.plg_authentication_example.ini
+/administrator/language/en-GB/en-GB.plg_authentication_example.sys.ini
+/administrator/language/en-GB/en-GB.plg_authentication_gmail.ini
+/administrator/language/en-GB/en-GB.plg_authentication_gmail.sys.ini
+/administrator/language/en-GB/en-GB.plg_authentication_joomla.ini
+/administrator/language/en-GB/en-GB.plg_authentication_joomla.sys.ini
+/administrator/language/en-GB/en-GB.plg_authentication_ldap.ini
+/administrator/language/en-GB/en-GB.plg_authentication_ldap.sys.ini
+/administrator/language/en-GB/en-GB.plg_captcha_recaptcha.ini
+/administrator/language/en-GB/en-GB.plg_captcha_recaptcha.sys.ini
+/administrator/language/en-GB/en-GB.plg_content_emailcloak.ini
+/administrator/language/en-GB/en-GB.plg_content_emailcloak.sys.ini
+/administrator/language/en-GB/en-GB.plg_content_geshi.ini
+/administrator/language/en-GB/en-GB.plg_content_geshi.sys.ini
+/administrator/language/en-GB/en-GB.plg_content_joomla.ini
+/administrator/language/en-GB/en-GB.plg_content_joomla.sys.ini
+/administrator/language/en-GB/en-GB.plg_content_loadmodule.ini
+/administrator/language/en-GB/en-GB.plg_content_loadmodule.sys.ini
+/administrator/language/en-GB/en-GB.plg_content_pagebreak.ini
+/administrator/language/en-GB/en-GB.plg_content_pagebreak.sys.ini
+/administrator/language/en-GB/en-GB.plg_content_pagenavigation.ini
+/administrator/language/en-GB/en-GB.plg_content_pagenavigation.sys.ini
+/administrator/language/en-GB/en-GB.plg_content_vote.ini
+/administrator/language/en-GB/en-GB.plg_content_vote.sys.ini
+/administrator/language/en-GB/en-GB.plg_editors_codemirror.ini
+/administrator/language/en-GB/en-GB.plg_editors_codemirror.sys.ini
+/administrator/language/en-GB/en-GB.plg_editors_none.ini
+/administrator/language/en-GB/en-GB.plg_editors_none.sys.ini
+/administrator/language/en-GB/en-GB.plg_editors_tinymce.ini
+/administrator/language/en-GB/en-GB.plg_editors_tinymce.sys.ini
+/administrator/language/en-GB/en-GB.plg_editors-xtd_article.ini
+/administrator/language/en-GB/en-GB.plg_editors-xtd_article.sys.ini
+/administrator/language/en-GB/en-GB.plg_editors-xtd_image.ini
+/administrator/language/en-GB/en-GB.plg_editors-xtd_image.sys.ini
+/administrator/language/en-GB/en-GB.plg_editors-xtd_pagebreak.ini
+/administrator/language/en-GB/en-GB.plg_editors-xtd_pagebreak.sys.ini
+/administrator/language/en-GB/en-GB.plg_editors-xtd_readmore.ini
+/administrator/language/en-GB/en-GB.plg_editors-xtd_readmore.sys.ini
+/administrator/language/en-GB/en-GB.plg_extension_joomla.ini
+/administrator/language/en-GB/en-GB.plg_extension_joomla.sys.ini
+/administrator/language/en-GB/en-GB.plg_quickicon_extensionupdate.ini
+/administrator/language/en-GB/en-GB.plg_quickicon_extensionupdate.sys.ini
+/administrator/language/en-GB/en-GB.plg_search_categories.ini
+/administrator/language/en-GB/en-GB.plg_search_categories.sys.ini
+/administrator/language/en-GB/en-GB.plg_search_contacts.ini
+/administrator/language/en-GB/en-GB.plg_search_contacts.sys.ini
+/administrator/language/en-GB/en-GB.plg_search_content.ini
+/administrator/language/en-GB/en-GB.plg_search_content.sys.ini
+/administrator/language/en-GB/en-GB.plg_search_newsfeeds.ini
+/administrator/language/en-GB/en-GB.plg_search_newsfeeds.sys.ini
+/administrator/language/en-GB/en-GB.plg_search_weblinks.ini
+/administrator/language/en-GB/en-GB.plg_search_weblinks.sys.ini
+/administrator/language/en-GB/en-GB.plg_system_cache.ini
+/administrator/language/en-GB/en-GB.plg_system_cache.sys.ini
+/administrator/language/en-GB/en-GB.plg_system_debug.ini
+/administrator/language/en-GB/en-GB.plg_system_debug.sys.ini
+/administrator/language/en-GB/en-GB.plg_system_highlight.ini
+/administrator/language/en-GB/en-GB.plg_system_highlight.sys.ini
+/administrator/language/en-GB/en-GB.plg_system_languagefilter.ini
+/administrator/language/en-GB/en-GB.plg_system_languagefilter.sys.ini
+/administrator/language/en-GB/en-GB.plg_system_log.ini
+/administrator/language/en-GB/en-GB.plg_system_logout.ini
+/administrator/language/en-GB/en-GB.plg_system_logout.sys.ini
+/administrator/language/en-GB/en-GB.plg_system_log.sys.ini
+/administrator/language/en-GB/en-GB.plg_system_p3p.ini
+/administrator/language/en-GB/en-GB.plg_system_p3p.sys.ini
+/administrator/language/en-GB/en-GB.plg_system_redirect.ini
+/administrator/language/en-GB/en-GB.plg_system_redirect.sys.ini
+/administrator/language/en-GB/en-GB.plg_system_remember.ini
+/administrator/language/en-GB/en-GB.plg_system_remember.sys.ini
+/administrator/language/en-GB/en-GB.plg_system_sef.ini
+/administrator/language/en-GB/en-GB.plg_system_sef.sys.ini
+/administrator/language/en-GB/en-GB.plg_user_contactcreator.ini
+/administrator/language/en-GB/en-GB.plg_user_contactcreator.sys.ini
+/administrator/language/en-GB/en-GB.plg_user_joomla.ini
+/administrator/language/en-GB/en-GB.plg_user_joomla.sys.ini
+/administrator/language/en-GB/en-GB.plg_user_profile.ini
+/administrator/language/en-GB/en-GB.plg_user_profile.sys.ini
+/administrator/language/en-GB/en-GB.tpl_bluestork.ini
+/administrator/language/en-GB/en-GB.tpl_bluestork.sys.ini
+/administrator/language/en-GB/en-GB.tpl_hathor.ini
+/administrator/language/en-GB/en-GB.tpl_hathor.sys.ini
+/administrator/language/en-GB/en-GB.xml
+/administrator/language/en-GB/index.html
+/administrator/language/overrides/*
+/administrator/language/index.html
+/administrator/manifests/*
+/administrator/modules/mod_custom/*
+/administrator/modules/mod_feed/*
+/administrator/modules/mod_latest/*
+/administrator/modules/mod_logged/*
+/administrator/modules/mod_login/*
+/administrator/modules/mod_menu/*
+/administrator/modules/mod_multilangstatus/*
+/administrator/modules/mod_online/*
+/administrator/modules/mod_popular/*
+/administrator/modules/mod_quickicon/*
+/administrator/modules/mod_status/*
+/administrator/modules/mod_submenu/*
+/administrator/modules/mod_title/*
+/administrator/modules/mod_toolbar/*
+/administrator/modules/mod_unread/*
+/administrator/modules/mod_version/*
+/administrator/modules/mod_stats_admin/*
+/administrator/modules/index.html
+/administrator/templates/bluestork/*
+/administrator/templates/isis/*
+/administrator/templates/hathor/*
+/administrator/templates/system/*
+/administrator/templates/index.html
+/administrator/index.php
+/cache/*
+/bin/*
+/cli/*
+/components/com_banners/*
+/components/com_ajax/*
+/components/com_config/*
+/components/com_contenthistory/*
+/components/com_tags/*
+/components/com_contact/*
+/components/com_content/*
+/components/com_finder/*
+/components/com_mailto/*
+/components/com_media/*
+/components/com_newsfeeds/*
+/components/com_search/*
+/components/com_users/*
+/components/com_weblinks/*
+/components/com_wrapper/*
+/components/index.html
+/images/banners/*
+/images/headers/*
+/images/sampledata/*
+/images/joomla*
+/images/index.html
+/images/powered_by.png
+/includes/*
+/installation/*
+/language/en-GB/en-GB.com_ajax.ini
+/language/en-GB/en-GB.com_config.ini
+/language/en-GB/en-GB.com_contact.ini
+/language/en-GB/en-GB.com_finder.ini
+/language/en-GB/en-GB.com_tags.ini
+/language/en-GB/en-GB.finder_cli.ini
+/language/en-GB/en-GB.lib_fof.sys.ini
+/language/en-GB/en-GB.lib_fof.ini
+/language/en-GB/en-GB.com_content.ini
+/language/en-GB/en-GB.lib_idna_convert.sys.ini
+/language/en-GB/en-GB.com_mailto.ini
+/language/en-GB/en-GB.lib_joomla.sys.ini
+/language/en-GB/en-GB.lib_phpass.sys.ini
+/language/en-GB/en-GB.lib_phpmailer.sys.ini
+/language/en-GB/en-GB.lib_phputf8.sys.ini
+/language/en-GB/en-GB.lib_simplepie.sys.ini
+/language/en-GB/en-GB.com_media.ini
+/language/en-GB/en-GB.mod_finder.ini
+/language/en-GB/en-GB.com_messages.ini
+/language/en-GB/en-GB.mod_tags_popular.ini
+/language/en-GB/en-GB.mod_tags_popular.sys.ini
+/language/en-GB/en-GB.mod_tags_similar.ini
+/language/en-GB/en-GB.mod_tags_similar.sys.ini
+/language/en-GB/en-GB.mod_finder.sys.ini
+/language/en-GB/en-GB.tpl_beez3.ini
+/language/en-GB/en-GB.tpl_beez3.sys.ini
+/language/en-GB/en-GB.com_newsfeeds.ini
+/language/en-GB/en-GB.tpl_protostar.ini
+/language/en-GB/en-GB.tpl_protostar.sys.ini
+/language/en-GB/en-GB.com_search.ini
+/language/en-GB/en-GB.com_users.ini
+/language/en-GB/en-GB.com_weblinks.ini
+/language/en-GB/en-GB.com_wrapper.ini
+/language/en-GB/en-GB.files_joomla.sys.ini
+/language/en-GB/en-GB.ini
+/language/en-GB/en-GB.lib_joomla.ini
+/language/en-GB/en-GB.localise.php
+/language/en-GB/en-GB.mod_articles_archive.ini
+/language/en-GB/en-GB.mod_articles_archive.sys.ini
+/language/en-GB/en-GB.mod_articles_categories.ini
+/language/en-GB/en-GB.mod_articles_categories.sys.ini
+/language/en-GB/en-GB.mod_articles_category.ini
+/language/en-GB/en-GB.mod_articles_category.sys.ini
+/language/en-GB/en-GB.mod_articles_latest.ini
+/language/en-GB/en-GB.mod_articles_latest.sys.ini
+/language/en-GB/en-GB.mod_articles_news.ini
+/language/en-GB/en-GB.mod_articles_news.sys.ini
+/language/en-GB/en-GB.mod_articles_popular.ini
+/language/en-GB/en-GB.mod_articles_popular.sys.ini
+/language/en-GB/en-GB.mod_banners.ini
+/language/en-GB/en-GB.mod_banners.sys.ini
+/language/en-GB/en-GB.mod_breadcrumbs.ini
+/language/en-GB/en-GB.mod_breadcrumbs.sys.ini
+/language/en-GB/en-GB.mod_custom.ini
+/language/en-GB/en-GB.mod_custom.sys.ini
+/language/en-GB/en-GB.mod_feed.ini
+/language/en-GB/en-GB.mod_feed.sys.ini
+/language/en-GB/en-GB.mod_footer.ini
+/language/en-GB/en-GB.mod_footer.sys.ini
+/language/en-GB/en-GB.mod_languages.ini
+/language/en-GB/en-GB.mod_languages.sys.ini
+/language/en-GB/en-GB.mod_login.ini
+/language/en-GB/en-GB.mod_login.sys.ini
+/language/en-GB/en-GB.mod_menu.ini
+/language/en-GB/en-GB.mod_menu.sys.ini
+/language/en-GB/en-GB.mod_random_image.ini
+/language/en-GB/en-GB.mod_random_image.sys.ini
+/language/en-GB/en-GB.mod_related_items.ini
+/language/en-GB/en-GB.mod_related_items.sys.ini
+/language/en-GB/en-GB.mod_search.ini
+/language/en-GB/en-GB.mod_search.sys.ini
+/language/en-GB/en-GB.mod_stats.ini
+/language/en-GB/en-GB.mod_stats.sys.ini
+/language/en-GB/en-GB.mod_syndicate.ini
+/language/en-GB/en-GB.mod_syndicate.sys.ini
+/language/en-GB/en-GB.mod_users_latest.ini
+/language/en-GB/en-GB.mod_users_latest.sys.ini
+/language/en-GB/en-GB.mod_weblinks.ini
+/language/en-GB/en-GB.mod_weblinks.sys.ini
+/language/en-GB/en-GB.mod_whosonline.ini
+/language/en-GB/en-GB.mod_whosonline.sys.ini
+/language/en-GB/en-GB.mod_wrapper.ini
+/language/en-GB/en-GB.mod_wrapper.sys.ini
+/language/en-GB/en-GB.tpl_atomic.ini
+/language/en-GB/en-GB.tpl_atomic.sys.ini
+/language/en-GB/en-GB.tpl_beez_20.ini
+/language/en-GB/en-GB.tpl_beez_20.sys.ini
+/language/en-GB/en-GB.tpl_beez5.ini
+/language/en-GB/en-GB.tpl_beez5.sys.ini
+/language/en-GB/en-GB.xml
+/language/en-GB/index.html
+/language/en-GB/install.xml
+/language/overrides/*
+/language/index.html
+/layouts/joomla/*
+/layouts/libraries/*
+/layouts/plugins/*
+/layouts/index.html
+/libraries/cms.php
+/libraries/cms/*
+/libraries/fof/*
+/libraries/idna_convert/*
+/libraries/joomla/*
+/libraries/legacy/*
+/libraries/phpass/*
+/libraries/phpmailer/*
+/libraries/phputf8/*
+/libraries/simplepie/*
+/libraries/vendor/*
+/libraries/classmap.php
+/libraries/import.legacy.php
+/libraries/index.html
+/libraries/import.php
+/libraries/loader.php
+/libraries/platform.php
+/logs/*
+/media/cms/*
+/media/com_contenthistory/*
+/media/com_finder/*
+/media/com_joomlaupdate/*
+/media/com_wrapper/*
+/media/contacts/*
+/media/editors/*
+/media/jui/*
+/media/mailto/*
+/media/media/*
+/media/mod_languages/*
+/media/overrider/*
+/media/plg_quickicon_extensionupdate/*
+/media/plg_quickicon_joomlaupdate/*
+/media/plg_system_highlight/*
+/media/system/*
+/media/index.html
+/modules/mod_articles_archive/*
+/modules/mod_articles_categories/*
+/modules/mod_articles_category/*
+/modules/mod_articles_latest/*
+/modules/mod_articles_news/*
+/modules/mod_articles_popular/*
+/modules/mod_banners/*
+/modules/mod_breadcrumbs/*
+/modules/mod_custom/*
+/modules/mod_feed/*
+/modules/mod_finder/*
+/modules/mod_footer/*
+/modules/mod_languages/*
+/modules/mod_login/*
+/modules/mod_menu/*
+/modules/mod_random_image/*
+/modules/mod_related_items/*
+/modules/mod_search/*
+/modules/mod_stats/*
+/modules/mod_syndicate/*
+/modules/mod_tags_popular/*
+/modules/mod_tags_similar/*
+/modules/mod_users_latest/*
+/modules/mod_weblinks/*
+/modules/mod_whosonline/*
+/modules/mod_wrapper/*
+/modules/index.html
+/plugins/authentication/example/*
+/plugins/authentication/gmail/*
+/plugins/authentication/joomla/*
+/plugins/authentication/ldap/*
+/plugins/authentication/cookie/*
+/plugins/authentication/index.html
+/plugins/captcha/recaptcha/*
+/plugins/captcha/index.html
+/plugins/content/emailcloak/*
+/plugins/content/example/*
+/plugins/content/finder/*
+/plugins/content/geshi/*
+/plugins/content/joomla/*
+/plugins/content/loadmodule/*
+/plugins/content/pagebreak/*
+/plugins/content/pagenavigation/*
+/plugins/content/vote/*
+/plugins/content/contact/*
+/plugins/content/index.html
+/plugins/editors/codemirror/*
+/plugins/editors/none/*
+/plugins/editors/tinymce/*
+/plugins/editors/index.html
+/plugins/editors-xtd/article/*
+/plugins/editors-xtd/image/*
+/plugins/editors-xtd/pagebreak/*
+/plugins/editors-xtd/readmore/*
+/plugins/editors-xtd/index.html
+/plugins/extension/example/*
+/plugins/extension/joomla/*
+/plugins/extension/index.html
+/plugins/finder/index.html
+/plugins/finder/categories/*
+/plugins/finder/contacts/*
+/plugins/finder/content/*
+/plugins/finder/newsfeeds/*
+/plugins/finder/tags/*
+/plugins/finder/weblinks/*
+/plugins/installer/*
+/plugins/quickicon/extensionupdate/*
+/plugins/quickicon/joomlaupdate/*
+/plugins/quickicon/index.html
+/plugins/search/categories/*
+/plugins/search/contacts/*
+/plugins/search/content/*
+/plugins/search/newsfeeds/*
+/plugins/search/weblinks/*
+/plugins/search/tags/*
+/plugins/search/index.html
+/plugins/system/cache/*
+/plugins/system/debug/*
+/plugins/system/highlight/*
+/plugins/system/languagecode/*
+/plugins/system/languagefilter/*
+/plugins/system/log/*
+/plugins/system/logout/*
+/plugins/system/p3p/*
+/plugins/system/redirect/*
+/plugins/system/remember/*
+/plugins/system/sef/*
+/plugins/system/index.html
+/plugins/twofactorauth/*
+/plugins/user/contactcreator/*
+/plugins/user/example/*
+/plugins/user/joomla/*
+/plugins/user/profile/*
+/plugins/user/index.html
+/plugins/index.html
+/templates/atomic/*
+/templates/beez3/*
+/templates/beez_20/*
+/templates/beez5/*
+/templates/protostar/*
+/templates/system/*
+/templates/index.html
+/tmp/*
+/configuration.php
+/index.php
+/joomla.xml
+/*.txt
+/robots.txt.dist
diff --git a/vendor/gitignore/KiCad.gitignore b/vendor/gitignore/KiCad.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..606ed1c7b4dbd9ed1636d789d7b91d62ff84d336
--- /dev/null
+++ b/vendor/gitignore/KiCad.gitignore
@@ -0,0 +1,20 @@
+# For PCBs designed using KiCad: http://www.kicad-pcb.org/
+
+# Temporary files
+*.000
+*.bak
+*.bck
+*.kicad_pcb-bak
+*~
+_autosave-*
+*.tmp
+
+# Netlist files (exported from Eeschema)
+*.net
+
+# Autorouter files (exported from Pcbnew)
+.dsn
+
+# Exported BOM files
+*.xml
+*.csv
diff --git a/vendor/gitignore/Kohana.gitignore b/vendor/gitignore/Kohana.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..8b2ab01a8004afafdcc3c50f0faed4a7eb0b64f6
--- /dev/null
+++ b/vendor/gitignore/Kohana.gitignore
@@ -0,0 +1,2 @@
+application/cache/*
+application/logs/*
diff --git a/vendor/gitignore/LabVIEW.gitignore b/vendor/gitignore/LabVIEW.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..122450865cf1ec6b1d0515cf46a4e5be73553031
--- /dev/null
+++ b/vendor/gitignore/LabVIEW.gitignore
@@ -0,0 +1,16 @@
+# Libraries
+*.lvlibp
+*.llb
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+
+# Metadata
+*.aliases
+*.lvlps
diff --git a/vendor/gitignore/Laravel.gitignore b/vendor/gitignore/Laravel.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..c491fa2bc6fef89af327dcd12c1373fcb4125d9a
--- /dev/null
+++ b/vendor/gitignore/Laravel.gitignore
@@ -0,0 +1,16 @@
+vendor/
+node_modules/
+
+# Laravel 4 specific
+bootstrap/compiled.php
+app/storage/
+
+# Laravel 5 & Lumen specific
+bootstrap/cache/
+storage/
+.env.*.php
+.env.php
+.env
+
+# Rocketeer PHP task runner and deployment package. https://github.com/rocketeers/rocketeer
+.rocketeer/
diff --git a/vendor/gitignore/Leiningen.gitignore b/vendor/gitignore/Leiningen.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..47fed6c20d9b87dfe78dc4817e198584cfa5f947
--- /dev/null
+++ b/vendor/gitignore/Leiningen.gitignore
@@ -0,0 +1,12 @@
+pom.xml
+pom.xml.asc
+*jar
+/lib/
+/classes/
+/target/
+/checkouts/
+.lein-deps-sum
+.lein-repl-history
+.lein-plugins/
+.lein-failures
+.nrepl-port
diff --git a/vendor/gitignore/LemonStand.gitignore b/vendor/gitignore/LemonStand.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..c7d94ad34b06f7e11238d19b6bd60361d4eebced
--- /dev/null
+++ b/vendor/gitignore/LemonStand.gitignore
@@ -0,0 +1,21 @@
+boot.php
+index.php
+install.php
+/config/*
+!/config/config.php
+/controllers/*
+/init/*
+/logs/*
+/phproad/*
+/temp/*
+/uploaded/*
+/installer_files/*
+/modules/backend/*
+/modules/blog/*
+/modules/cms/*
+/modules/core/*
+/modules/session/*
+/modules/shop/*
+/modules/system/*
+/modules/users/*
+# add content_*.php if you don't want erase client changes to content
diff --git a/vendor/gitignore/Lilypond.gitignore b/vendor/gitignore/Lilypond.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..513e6edd9c4a5bda43eb376c2f9a5d318eb135ec
--- /dev/null
+++ b/vendor/gitignore/Lilypond.gitignore
@@ -0,0 +1,6 @@
+*.pdf
+*.ps
+*.midi
+*.mid
+*.log
+*~
diff --git a/vendor/gitignore/Lithium.gitignore b/vendor/gitignore/Lithium.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..7b22568ea890623c6c43f242ecd5bb0ac5ece6cf
--- /dev/null
+++ b/vendor/gitignore/Lithium.gitignore
@@ -0,0 +1,2 @@
+libraries/*
+resources/tmp/*
diff --git a/vendor/gitignore/Lua.gitignore b/vendor/gitignore/Lua.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..6fd0a376decfbf0a7be87fdc75d5109da72a7d17
--- /dev/null
+++ b/vendor/gitignore/Lua.gitignore
@@ -0,0 +1,41 @@
+# Compiled Lua sources
+luac.out
+
+# luarocks build files
+*.src.rock
+*.zip
+*.tar.gz
+
+# Object files
+*.o
+*.os
+*.ko
+*.obj
+*.elf
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+*.def
+*.exp
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
diff --git a/vendor/gitignore/Magento.gitignore b/vendor/gitignore/Magento.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..195c9b7a029bb497a99176ca7d846daa07b698ea
--- /dev/null
+++ b/vendor/gitignore/Magento.gitignore
@@ -0,0 +1,104 @@
+.htaccess.sample
+.modgit/
+.modman/
+app/code/community/Phoenix/Moneybookers/
+app/code/community/Cm/RedisSession/
+app/code/core/
+app/design/adminhtml/default/default/
+app/design/frontend/base/
+app/design/frontend/rwd/
+app/design/frontend/default/blank/
+app/design/frontend/default/default/
+app/design/frontend/default/iphone/
+app/design/frontend/default/modern/
+app/design/frontend/enterprise/default
+app/design/install/
+app/etc/modules/Enterprise_*
+app/etc/modules/Mage_*.xml
+app/etc/modules/Phoenix_Moneybookers.xml
+app/etc/modules/Cm_RedisSession.xml
+app/etc/applied.patches.list
+app/etc/config.xml
+app/etc/enterprise.xml
+app/etc/local.xml.additional
+app/etc/local.xml.template
+app/etc/local.xml
+app/.htaccess
+app/bootstrap.php
+app/locale/en_US/
+app/Mage.php
+/cron.php
+cron.sh
+dev/.htaccess
+dev/tests/functional/
+downloader/
+errors/
+favicon.ico
+/get.php
+includes/
+/index.php
+index.php.sample
+/install.php
+js/blank.html
+js/calendar/
+js/enterprise/
+js/extjs/
+js/firebug/
+js/flash/
+js/index.php
+js/jscolor/
+js/lib/
+js/mage/
+js/prototype/
+js/scriptaculous/
+js/spacer.gif
+js/tiny_mce/
+js/varien/
+lib/3Dsecure/
+lib/Apache/
+lib/flex/
+lib/googlecheckout/
+lib/.htaccess
+lib/LinLibertineFont/
+lib/Mage/
+lib/PEAR/
+lib/Pelago/
+lib/phpseclib/
+lib/Varien/
+lib/Zend/
+lib/Cm/
+lib/Credis/
+lib/Magento/
+LICENSE_AFL.txt
+LICENSE.html
+LICENSE.txt
+LICENSE_EE*
+/mage
+media/
+/api.php
+nbproject/
+pear
+pear/
+php.ini.sample
+pkginfo/
+RELEASE_NOTES.txt
+shell/.htaccess
+shell/abstract.php
+shell/compiler.php
+shell/indexer.php
+shell/log.php
+sitemap.xml
+skin/adminhtml/default/default/
+skin/adminhtml/default/enterprise
+skin/frontend/base/
+skin/frontend/rwd/
+skin/frontend/default/blank/
+skin/frontend/default/blue/
+skin/frontend/default/default/
+skin/frontend/default/french/
+skin/frontend/default/german/
+skin/frontend/default/iphone/
+skin/frontend/default/modern/
+skin/frontend/enterprise
+skin/install/
+var/
diff --git a/vendor/gitignore/Maven.gitignore b/vendor/gitignore/Maven.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..1cdc9f7fd45e8003ad15bea0eaf7c76cd09c8e42
--- /dev/null
+++ b/vendor/gitignore/Maven.gitignore
@@ -0,0 +1,9 @@
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+dependency-reduced-pom.xml
+buildNumber.properties
+.mvn/timing.properties
diff --git a/vendor/gitignore/Mercury.gitignore b/vendor/gitignore/Mercury.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..70ec86939718241f046522a8cc0143d5deeecaea
--- /dev/null
+++ b/vendor/gitignore/Mercury.gitignore
@@ -0,0 +1,13 @@
+Mercury/
+Mercury.modules
+*.mh
+*.err
+*.init
+*.dll
+*.exe
+*.a
+*.so
+*.dylib
+*.beams
+*.d
+*.c_date
diff --git a/vendor/gitignore/MetaProgrammingSystem.gitignore b/vendor/gitignore/MetaProgrammingSystem.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..3e75841041c283547b25fdc71e35f347f366518e
--- /dev/null
+++ b/vendor/gitignore/MetaProgrammingSystem.gitignore
@@ -0,0 +1,16 @@
+workspace.xml
+junitvmwatcher*.properties
+build.properties
+
+# generated java classes and java source files
+#   manually add any custom artifacts that can't be generated from the models
+#   http://confluence.jetbrains.com/display/MPSD25/HowTo+--+MPS+and+Git
+classes_gen
+source_gen
+source_gen.caches
+
+# generated test code and test results
+test_gen
+test_gen.caches
+TEST-*.xml
+junit*.properties
diff --git a/vendor/gitignore/Nanoc.gitignore b/vendor/gitignore/Nanoc.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..abc21828a3ed1662bb28af14afb92b5b7efccb4c
--- /dev/null
+++ b/vendor/gitignore/Nanoc.gitignore
@@ -0,0 +1,10 @@
+# For projects using nanoc (http://nanoc.ws/)
+
+# Default location for output, needs to match output_dir's value found in config.yaml
+output/
+
+# Temporary file directory
+tmp/
+
+# Crash Log
+crash.log
diff --git a/vendor/gitignore/Nim.gitignore b/vendor/gitignore/Nim.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..67d9b34c6cecad82ad17197ffa5db4860caf9037
--- /dev/null
+++ b/vendor/gitignore/Nim.gitignore
@@ -0,0 +1 @@
+nimcache/
diff --git a/vendor/gitignore/Node.gitignore b/vendor/gitignore/Node.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..5148e527a7e286a1efcc44d65a7f8241267dce9b
--- /dev/null
+++ b/vendor/gitignore/Node.gitignore
@@ -0,0 +1,37 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules
+jspm_packages
+
+# Optional npm cache directory
+.npm
+
+# Optional REPL history
+.node_repl_history
diff --git a/vendor/gitignore/OCaml.gitignore b/vendor/gitignore/OCaml.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..f7817ae5c36f9ddef21eb6c9796b5c35976cc80f
--- /dev/null
+++ b/vendor/gitignore/OCaml.gitignore
@@ -0,0 +1,20 @@
+*.annot
+*.cmo
+*.cma
+*.cmi
+*.a
+*.o
+*.cmx
+*.cmxs
+*.cmxa
+
+# ocamlbuild working directory
+_build/
+
+# ocamlbuild targets
+*.byte
+*.native
+
+# oasis generated files
+setup.data
+setup.log
diff --git a/vendor/gitignore/Objective-C.gitignore b/vendor/gitignore/Objective-C.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..3020bc327a7ce6cbdbcda32fe56deaf58f82654a
--- /dev/null
+++ b/vendor/gitignore/Objective-C.gitignore
@@ -0,0 +1,51 @@
+# Xcode
+#
+# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
+
+## Build generated
+build/
+DerivedData/
+
+## Various settings
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata/
+
+## Other
+*.moved-aside
+*.xcuserstate
+
+## Obj-C/Swift specific
+*.hmap
+*.ipa
+
+# CocoaPods
+#
+# We recommend against adding the Pods directory to your .gitignore. However
+# you should judge for yourself, the pros and cons are mentioned at:
+# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
+#
+# Pods/
+
+# Carthage
+#
+# Add this line if you want to avoid checking in source code from Carthage dependencies.
+# Carthage/Checkouts
+
+Carthage/Build
+
+# fastlane
+#
+# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 
+# screenshots whenever they are needed.
+# For more information about the recommended setup visit:
+# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
+
+fastlane/report.xml
+fastlane/screenshots
diff --git a/vendor/gitignore/Opa.gitignore b/vendor/gitignore/Opa.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..74c6219ceda9291aec7f74386d27ec7b75580844
--- /dev/null
+++ b/vendor/gitignore/Opa.gitignore
@@ -0,0 +1,13 @@
+_build
+_tracks
+
+opa-debug-js
+
+*.opp
+*.opx
+*.opx.broken
+*.dump
+*.api
+*.api-txt
+*.exe
+*.log
diff --git a/vendor/gitignore/OpenCart.gitignore b/vendor/gitignore/OpenCart.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..28e45aa6aac4a1f9fffe82ad52e4169bbd1a25f0
--- /dev/null
+++ b/vendor/gitignore/OpenCart.gitignore
@@ -0,0 +1,13 @@
+.htaccess
+/config.php
+admin/config.php
+
+!index.html
+
+download/
+image/data/
+image/cache/
+system/cache/
+system/logs/
+
+system/storage/
diff --git a/vendor/gitignore/OracleForms.gitignore b/vendor/gitignore/OracleForms.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..699a494011875395b4247a15bbcb909f1b38e5a7
--- /dev/null
+++ b/vendor/gitignore/OracleForms.gitignore
@@ -0,0 +1,8 @@
+# Compiled Form Modules
+*.fmx
+
+# Compiled Menu Modules
+*.mmx
+
+# Compiled Pre-Linked Libraries
+*.plx
diff --git a/vendor/gitignore/Packer.gitignore b/vendor/gitignore/Packer.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..1b7a03efdd72ff50f9e4c725720ea6ccad2b8174
--- /dev/null
+++ b/vendor/gitignore/Packer.gitignore
@@ -0,0 +1,5 @@
+# Cache objects
+packer_cache/
+
+# For built boxes
+*.box
diff --git a/vendor/gitignore/Perl.gitignore b/vendor/gitignore/Perl.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..ae2ad536abbc81c49dad4aca4f308277032a7ad0
--- /dev/null
+++ b/vendor/gitignore/Perl.gitignore
@@ -0,0 +1,20 @@
+/blib/
+/.build/
+_build/
+cover_db/
+inc/
+Build
+!Build/
+Build.bat
+.last_cover_stats
+/Makefile
+/Makefile.old
+/MANIFEST.bak
+/META.yml
+/META.json
+/MYMETA.*
+nytprof.out
+/pm_to_blib
+*.o
+*.bs
+/_eumm/
diff --git a/vendor/gitignore/Phalcon.gitignore b/vendor/gitignore/Phalcon.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..6ffe3aa220a9838f75900cf00e755ced29859d7a
--- /dev/null
+++ b/vendor/gitignore/Phalcon.gitignore
@@ -0,0 +1,2 @@
+/cache/
+/config/development/
diff --git a/vendor/gitignore/PlayFramework.gitignore b/vendor/gitignore/PlayFramework.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..6d67f1191751bc6de7f13d7c7c10399c31c7b38b
--- /dev/null
+++ b/vendor/gitignore/PlayFramework.gitignore
@@ -0,0 +1,15 @@
+# Ignore Play! working directory #
+bin/
+/db
+.eclipse
+/lib/
+/logs/
+/modules
+/project/target
+/target
+tmp/
+test-result
+server.pid
+*.eml
+/dist/
+.cache
diff --git a/vendor/gitignore/Plone.gitignore b/vendor/gitignore/Plone.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..770a8681ac36ee996cb0a45a1ff90e160f4ba267
--- /dev/null
+++ b/vendor/gitignore/Plone.gitignore
@@ -0,0 +1,18 @@
+*.pyc
+*.pyo
+*.tmp*
+*.mo
+*.egg
+*.EGG
+*.egg-info
+*.EGG-INFO
+.*.cfg
+bin/
+build/
+develop-eggs/
+downloads/
+eggs/
+fake-eggs/
+parts/
+dist/
+var/
diff --git a/vendor/gitignore/Prestashop.gitignore b/vendor/gitignore/Prestashop.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..7c6ae1e31ccda703771aaf64f7c26205c73ccc3d
--- /dev/null
+++ b/vendor/gitignore/Prestashop.gitignore
@@ -0,0 +1,32 @@
+# Private files
+# The following files contain your database credentials and other personal data.
+
+config/settings.*.php
+
+# Cache, temp and generated files
+# The following files are generated by PrestaShop.
+
+admin-dev/autoupgrade/
+/cache/
+!/cache/index.php
+!/cache/cachefs/index.php
+!/cache/purifier/index.php
+!/cache/push/index.php
+!/cache/sandbox/index.php
+!/cache/smarty/index.php
+!/cache/tcpdf/index.php
+config/xml/*.xml
+/log/*
+*sitemap.xml
+themes/*/cache/
+modules/*/config*.xml
+
+# Site content
+# The following folders contain product images, virtual products, CSV's, etc.
+
+admin-dev/backups/
+admin-dev/export/
+admin-dev/import/
+download/
+/img/*
+upload/
diff --git a/vendor/gitignore/Processing.gitignore b/vendor/gitignore/Processing.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..85f269a89f6091d096f6924cd99777c885cca8d7
--- /dev/null
+++ b/vendor/gitignore/Processing.gitignore
@@ -0,0 +1,7 @@
+.DS_Store
+applet
+application.linux32
+application.linux64
+application.windows32
+application.windows64
+application.macosx
diff --git a/vendor/gitignore/Python.gitignore b/vendor/gitignore/Python.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..72364f99fe4bf8d5262df3b19b33102aeaa791e5
--- /dev/null
+++ b/vendor/gitignore/Python.gitignore
@@ -0,0 +1,89 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+.hypothesis/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# IPython Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# dotenv
+.env
+
+# virtualenv
+venv/
+ENV/
+
+# Spyder project settings
+.spyderproject
+
+# Rope project settings
+.ropeproject
diff --git a/vendor/gitignore/Qooxdoo.gitignore b/vendor/gitignore/Qooxdoo.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..d0c64102d85bb01cabfe12c34a5639e00c78060d
--- /dev/null
+++ b/vendor/gitignore/Qooxdoo.gitignore
@@ -0,0 +1,5 @@
+cache
+cache-downloads
+inspector
+api
+source/inspector.html
diff --git a/vendor/gitignore/Qt.gitignore b/vendor/gitignore/Qt.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..fa24b2efee8cce6c12437e021c50aeeb12313d24
--- /dev/null
+++ b/vendor/gitignore/Qt.gitignore
@@ -0,0 +1,38 @@
+# C++ objects and libs
+
+*.slo
+*.lo
+*.o
+*.a
+*.la
+*.lai
+*.so
+*.dll
+*.dylib
+
+# Qt-es
+
+/.qmake.cache
+/.qmake.stash
+*.pro.user
+*.pro.user.*
+*.qbs.user
+*.qbs.user.*
+*.moc
+moc_*.cpp
+qrc_*.cpp
+ui_*.h
+Makefile*
+*build-*
+
+# QtCreator
+
+*.autosave
+
+# QtCtreator Qml
+*.qmlproject.user
+*.qmlproject.user.*
+
+# QtCtreator CMake
+CMakeLists.txt.user
+
diff --git a/vendor/gitignore/R.gitignore b/vendor/gitignore/R.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..fcff087aebb621ca6fd0351733d304943d91a0d3
--- /dev/null
+++ b/vendor/gitignore/R.gitignore
@@ -0,0 +1,33 @@
+# History files
+.Rhistory
+.Rapp.history
+
+# Session Data files
+.RData
+
+# Example code in package build process
+*-Ex.R
+
+# Output files from R CMD build
+/*.tar.gz
+
+# Output files from R CMD check
+/*.Rcheck/
+
+# RStudio files
+.Rproj.user/
+
+# produced vignettes
+vignettes/*.html
+vignettes/*.pdf
+
+# OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3
+.httr-oauth
+
+# knitr and R markdown default cache directories
+/*_cache/
+/cache/
+
+# Temporary files created by R markdown
+*.utf8.md
+*.knit.md
diff --git a/vendor/gitignore/README.md b/vendor/gitignore/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..43131e815cca2ecc85ee396e3a239057171ac2de
--- /dev/null
+++ b/vendor/gitignore/README.md
@@ -0,0 +1,14 @@
+# .gitignore templates
+
+This directory contains language-specific .gitignore templates that are used by GitLab.
+
+These files were automatically pulled from [this repository](https://github.com/github/gitignore).
+Please submit pull requests to that repository. There is no need to edit the files in this directory.
+
+## Bulk Update
+
+To update this directory with the latest changes in the repository, run:
+
+```sh
+bundle exec rake gitlab:update_gitignore
+```
diff --git a/vendor/gitignore/ROS.gitignore b/vendor/gitignore/ROS.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..f8bcd11737109a2cc30684b5431020a0f370e8cf
--- /dev/null
+++ b/vendor/gitignore/ROS.gitignore
@@ -0,0 +1,47 @@
+build/
+bin/
+lib/
+msg_gen/
+srv_gen/
+msg/*Action.msg
+msg/*ActionFeedback.msg
+msg/*ActionGoal.msg
+msg/*ActionResult.msg
+msg/*Feedback.msg
+msg/*Goal.msg
+msg/*Result.msg
+msg/_*.py
+
+# Generated by dynamic reconfigure
+*.cfgc
+/cfg/cpp/
+/cfg/*.py
+
+# Ignore generated docs
+*.dox
+*.wikidoc
+
+# eclipse stuff
+.project
+.cproject
+
+# qcreator stuff
+CMakeLists.txt.user
+
+srv/_*.py
+*.pcd
+*.pyc
+qtcreator-*
+*.user
+
+/planning/cfg
+/planning/docs
+/planning/src
+
+*~
+
+# Emacs
+.#*
+
+# Catkin custom files
+CATKIN_IGNORE
diff --git a/vendor/gitignore/Rails.gitignore b/vendor/gitignore/Rails.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..2121e0a8038ff598480289af8d9bedd0a8b290fb
--- /dev/null
+++ b/vendor/gitignore/Rails.gitignore
@@ -0,0 +1,38 @@
+*.rbc
+capybara-*.html
+.rspec
+/log
+/tmp
+/db/*.sqlite3
+/db/*.sqlite3-journal
+/public/system
+/coverage/
+/spec/tmp
+**.orig
+rerun.txt
+pickle-email-*.html
+
+# TODO Comment out these rules if you are OK with secrets being uploaded to the repo
+config/initializers/secret_token.rb
+config/secrets.yml
+
+## Environment normalization:
+/.bundle
+/vendor/bundle
+
+# these should all be checked in to normalize the environment:
+# Gemfile.lock, .ruby-version, .ruby-gemset
+
+# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
+.rvmrc
+
+# if using bower-rails ignore default bower_components path bower.json files
+/vendor/assets/bower_components
+*.bowerrc
+bower.json
+
+# Ignore pow environment settings
+.powenv
+
+# Ignore Byebug command history file.
+.byebug_history
diff --git a/vendor/gitignore/RhodesRhomobile.gitignore b/vendor/gitignore/RhodesRhomobile.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..a211dcc3b0f7f791892ebc2a21048982403a5efc
--- /dev/null
+++ b/vendor/gitignore/RhodesRhomobile.gitignore
@@ -0,0 +1,9 @@
+rholog-*
+sim-*
+bin/libs
+bin/RhoBundle
+bin/tmp
+bin/target
+bin/*.ap_
+*.o
+*.jar
diff --git a/vendor/gitignore/Ruby.gitignore b/vendor/gitignore/Ruby.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..5e1422c9c3f1ddaae47b5b1b597bc80748611993
--- /dev/null
+++ b/vendor/gitignore/Ruby.gitignore
@@ -0,0 +1,50 @@
+*.gem
+*.rbc
+/.config
+/coverage/
+/InstalledFiles
+/pkg/
+/spec/reports/
+/spec/examples.txt
+/test/tmp/
+/test/version_tmp/
+/tmp/
+
+# Used by dotenv library to load environment variables.
+# .env
+
+## Specific to RubyMotion:
+.dat*
+.repl_history
+build/
+*.bridgesupport
+build-iPhoneOS/
+build-iPhoneSimulator/
+
+## Specific to RubyMotion (use of CocoaPods):
+#
+# We recommend against adding the Pods directory to your .gitignore. However
+# you should judge for yourself, the pros and cons are mentioned at:
+# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
+#
+# vendor/Pods/
+
+## Documentation cache and generated files:
+/.yardoc/
+/_yardoc/
+/doc/
+/rdoc/
+
+## Environment normalization:
+/.bundle/
+/vendor/bundle
+/lib/bundler/man/
+
+# for a library or gem, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# Gemfile.lock
+# .ruby-version
+# .ruby-gemset
+
+# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
+.rvmrc
diff --git a/vendor/gitignore/Rust.gitignore b/vendor/gitignore/Rust.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..cb14a420640b9af2da76a8a73650fba0929bfeb8
--- /dev/null
+++ b/vendor/gitignore/Rust.gitignore
@@ -0,0 +1,7 @@
+# Generated by Cargo
+# will have compiled files and executables
+/target/
+
+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
+# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
+Cargo.lock
diff --git a/vendor/gitignore/SCons.gitignore b/vendor/gitignore/SCons.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..39d9743a082afc23f3d05055e1322f57a9cd5167
--- /dev/null
+++ b/vendor/gitignore/SCons.gitignore
@@ -0,0 +1,2 @@
+# for projects that use SCons for building: http://http://www.scons.org/
+.sconsign.dblite
diff --git a/vendor/gitignore/Sass.gitignore b/vendor/gitignore/Sass.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..486b32ce90c34784ec42e113266dd0770b881011
--- /dev/null
+++ b/vendor/gitignore/Sass.gitignore
@@ -0,0 +1,2 @@
+.sass-cache/
+*.css.map
diff --git a/vendor/gitignore/Scala.gitignore b/vendor/gitignore/Scala.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..c58d83b318909082a44d3a84222b74607d1268f8
--- /dev/null
+++ b/vendor/gitignore/Scala.gitignore
@@ -0,0 +1,17 @@
+*.class
+*.log
+
+# sbt specific
+.cache
+.history
+.lib/
+dist/*
+target/
+lib_managed/
+src_managed/
+project/boot/
+project/plugins/project/
+
+# Scala-IDE specific
+.scala_dependencies
+.worksheet
diff --git a/vendor/gitignore/Scheme.gitignore b/vendor/gitignore/Scheme.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..cbb89d78da51cb0087893b4069218550a6cd2886
--- /dev/null
+++ b/vendor/gitignore/Scheme.gitignore
@@ -0,0 +1,7 @@
+*.ss~
+*.ss#*
+.#*.ss
+
+*.scm~
+*.scm#*
+.#*.scm
diff --git a/vendor/gitignore/Scrivener.gitignore b/vendor/gitignore/Scrivener.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..3b39c66ba12347c2d598eb7a27a2fda86feb7b87
--- /dev/null
+++ b/vendor/gitignore/Scrivener.gitignore
@@ -0,0 +1,7 @@
+/Files/binder.autosave
+/Files/binder.backup
+/Files/search.indexes
+/Files/user.lock
+/Files/Docs/docs.checksum
+/QuickLook/
+/Settings/ui.plist
diff --git a/vendor/gitignore/Sdcc.gitignore b/vendor/gitignore/Sdcc.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..07ee7d59abafb0f5ab798356e8c2302574f7455a
--- /dev/null
+++ b/vendor/gitignore/Sdcc.gitignore
@@ -0,0 +1,8 @@
+# SDCC stuff
+*.lnk
+*.lst
+*.map
+*.mem
+*.rel
+*.rst
+*.sym
diff --git a/vendor/gitignore/SeamGen.gitignore b/vendor/gitignore/SeamGen.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..a418cf376c573a7923af942bccea53c3d512dfab
--- /dev/null
+++ b/vendor/gitignore/SeamGen.gitignore
@@ -0,0 +1,26 @@
+/bootstrap/data
+/bootstrap/tmp
+/classes/
+/dist/
+/exploded-archives/
+/test-build/
+/test-output/
+/test-report/
+/target/
+temp-testng-customsuite.xml
+
+# based on http://stackoverflow.com/a/8865858/422476 I am removing inline comments
+
+#/classes/  		              all class files
+#/dist/                       contains generated war files for deployment
+#/exploded-archives/		      war content generation during deploy (or explode)
+#/test-build/                 test compilation (ant target for Seam)
+#/test-output/                test results
+#/test-report/                test report generation for, e.g., Hudson
+#/target/                     maven output folder
+#temp-testng-customsuite.xml	generated when running test cases under Eclipse
+
+# Thanks to @VonC and @kraftan for their helpful answers on a related question
+# on StackOverflow.com:
+# http://stackoverflow.com/questions/4176687
+# /what-is-the-recommended-source-control-ignore-pattern-for-seam-projects
diff --git a/vendor/gitignore/SketchUp.gitignore b/vendor/gitignore/SketchUp.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..5160df3c6bf8b351360ec6b4ff45003c84021cfe
--- /dev/null
+++ b/vendor/gitignore/SketchUp.gitignore
@@ -0,0 +1 @@
+*.skb
diff --git a/vendor/gitignore/Smalltalk.gitignore b/vendor/gitignore/Smalltalk.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..75272b234725a4c28072061be87f5885bfef9707
--- /dev/null
+++ b/vendor/gitignore/Smalltalk.gitignore
@@ -0,0 +1,18 @@
+# changes file
+*.changes
+
+# system image
+*.image
+
+# Pharo Smalltalk Debug log file
+PharoDebug.log
+
+# Squeak Smalltalk Debug log file
+SqueakDebug.log
+
+# Monticello package cache
+/package-cache
+
+# Metacello-github cache
+/github-cache
+github-*.zip
diff --git a/vendor/gitignore/Stella.gitignore b/vendor/gitignore/Stella.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..402a5438373542b72f749cc17b6901fa9372012e
--- /dev/null
+++ b/vendor/gitignore/Stella.gitignore
@@ -0,0 +1,12 @@
+# Atari 2600 (Stella) support for multiple assemblers
+# - DASM
+# - CC65
+
+# Assembled binaries and object directories
+obj/
+a.out
+*.bin
+*.a26
+
+# Add in special Atari 7800-based binaries for good measure
+*.a78
diff --git a/vendor/gitignore/SugarCRM.gitignore b/vendor/gitignore/SugarCRM.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..842c3ec518bf64ba78621449b076c919b4d5d98c
--- /dev/null
+++ b/vendor/gitignore/SugarCRM.gitignore
@@ -0,0 +1,25 @@
+## SugarCRM
+# Ignore custom .htaccess stuff.
+/.htaccess
+# Ignore the cache directory completely.
+# This will break the current behaviour. Which was often leading to
+# the misuse of the repository as backup replacement.
+# For development the cache directory can be safely ignored and
+# therefore it is ignored.
+/cache/
+# Ignore some files and directories from the custom directory.
+/custom/history/
+/custom/modulebuilder/
+/custom/working/
+/custom/modules/*/Ext/
+/custom/application/Ext/
+# Custom configuration should also be ignored.
+/config.php
+/config_override.php
+# The silent upgrade scripts aren't needed.
+/silentUpgrade*.php
+# Logs files can safely be ignored.
+*.log
+# Ignore the new upload directories.
+/upload/
+/upload_backup/
diff --git a/vendor/gitignore/Swift.gitignore b/vendor/gitignore/Swift.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..8a29fa52af49ce255189b407970bfb8284eb29c1
--- /dev/null
+++ b/vendor/gitignore/Swift.gitignore
@@ -0,0 +1,63 @@
+# Xcode
+#
+# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
+
+## Build generated
+build/
+DerivedData/
+
+## Various settings
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata/
+
+## Other
+*.moved-aside
+*.xcuserstate
+
+## Obj-C/Swift specific
+*.hmap
+*.ipa
+
+## Playgrounds
+timeline.xctimeline
+playground.xcworkspace
+
+# Swift Package Manager
+#
+# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
+# Packages/
+.build/
+
+# CocoaPods
+#
+# We recommend against adding the Pods directory to your .gitignore. However
+# you should judge for yourself, the pros and cons are mentioned at:
+# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
+#
+# Pods/
+
+# Carthage
+#
+# Add this line if you want to avoid checking in source code from Carthage dependencies.
+# Carthage/Checkouts
+
+Carthage/Build
+
+# fastlane
+#
+# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
+# screenshots whenever they are needed.
+# For more information about the recommended setup visit:
+# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
+
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots
+fastlane/test_output
diff --git a/vendor/gitignore/Symfony.gitignore b/vendor/gitignore/Symfony.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..7d56f982f81f90b8502d42688408750a7afd736c
--- /dev/null
+++ b/vendor/gitignore/Symfony.gitignore
@@ -0,0 +1,48 @@
+# Cache and logs (Symfony2)
+/app/cache/*
+/app/logs/*
+!app/cache/.gitkeep
+!app/logs/.gitkeep
+
+# Email spool folder
+/app/spool/*
+
+# Cache, session files and logs (Symfony3)
+/var/cache/*
+/var/logs/*
+/var/sessions/*
+!var/cache/.gitkeep
+!var/logs/.gitkeep
+!var/sessions/.gitkeep
+
+# Parameters
+/app/config/parameters.yml
+/app/config/parameters.ini
+
+# Managed by Composer
+/app/bootstrap.php.cache
+/var/bootstrap.php.cache
+/bin/*
+!bin/console
+!bin/symfony_requirements
+/vendor/
+
+# Assets and user uploads
+/web/bundles/
+/web/uploads/
+
+# Assets managed by Bower
+/web/assets/vendor/
+
+# PHPUnit
+/app/phpunit.xml
+/phpunit.xml
+
+# Build data
+/build/
+
+# Composer PHAR
+/composer.phar
+
+# Backup entities generated with doctrine:generate:entities command
+*/Entity/*~
diff --git a/vendor/gitignore/SymphonyCMS.gitignore b/vendor/gitignore/SymphonyCMS.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..671c7ff9e32680d0a0ecf05dfc2c126f7023ca7f
--- /dev/null
+++ b/vendor/gitignore/SymphonyCMS.gitignore
@@ -0,0 +1,6 @@
+manifest/cache/
+manifest/logs/
+manifest/tmp/
+symphony/
+workspace/uploads/
+install-log.txt
diff --git a/vendor/gitignore/TeX.gitignore b/vendor/gitignore/TeX.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..4123a577c47370599d4f7cd9a559280cc7df6ab3
--- /dev/null
+++ b/vendor/gitignore/TeX.gitignore
@@ -0,0 +1,180 @@
+## Core latex/pdflatex auxiliary files:
+*.aux
+*.lof
+*.log
+*.lot
+*.fls
+*.out
+*.toc
+*.fmt
+*.fot
+*.cb
+*.cb2
+
+## Intermediate documents:
+*.dvi
+*-converted-to.*
+# these rules might exclude image files for figures etc.
+# *.ps
+# *.eps
+# *.pdf
+
+## Bibliography auxiliary files (bibtex/biblatex/biber):
+*.bbl
+*.bcf
+*.blg
+*-blx.aux
+*-blx.bib
+*.brf
+*.run.xml
+
+## Build tool auxiliary files:
+*.fdb_latexmk
+*.synctex
+*.synctex.gz
+*.synctex.gz(busy)
+*.pdfsync
+
+## Auxiliary and intermediate files from other packages:
+# algorithms
+*.alg
+*.loa
+
+# achemso
+acs-*.bib
+
+# amsthm
+*.thm
+
+# beamer
+*.nav
+*.snm
+*.vrb
+
+# cprotect
+*.cpt
+
+# fixme
+*.lox
+
+#(r)(e)ledmac/(r)(e)ledpar
+*.end
+*.?end
+*.[1-9]
+*.[1-9][0-9]
+*.[1-9][0-9][0-9]
+*.[1-9]R
+*.[1-9][0-9]R
+*.[1-9][0-9][0-9]R
+*.eledsec[1-9]
+*.eledsec[1-9]R
+*.eledsec[1-9][0-9]
+*.eledsec[1-9][0-9]R
+*.eledsec[1-9][0-9][0-9]
+*.eledsec[1-9][0-9][0-9]R
+
+# glossaries
+*.acn
+*.acr
+*.glg
+*.glo
+*.gls
+*.glsdefs
+
+# gnuplottex
+*-gnuplottex-*
+
+# hyperref
+*.brf
+
+# knitr
+*-concordance.tex
+# TODO Comment the next line if you want to keep your tikz graphics files
+*.tikz
+*-tikzDictionary
+
+# listings
+*.lol
+
+# makeidx
+*.idx
+*.ilg
+*.ind
+*.ist
+
+# minitoc
+*.maf
+*.mlf
+*.mlt
+*.mtc
+*.mtc[0-9]
+*.mtc[1-9][0-9]
+
+# minted
+_minted*
+*.pyg
+
+# morewrites
+*.mw
+
+# mylatexformat
+*.fmt
+
+# nomencl
+*.nlo
+
+# sagetex
+*.sagetex.sage
+*.sagetex.py
+*.sagetex.scmd
+
+# sympy
+*.sout
+*.sympy
+sympy-plots-for-*.tex/
+
+# pdfcomment
+*.upa
+*.upb
+
+# pythontex
+*.pytxcode
+pythontex-files-*/
+
+# thmtools
+*.loe
+
+# TikZ & PGF
+*.dpth
+*.md5
+*.auxlock
+
+# todonotes
+*.tdo
+
+# xindy
+*.xdy
+
+# xypic precompiled matrices
+*.xyc
+
+# endfloat
+*.ttt
+*.fff
+
+# Latexian
+TSWLatexianTemp*
+
+## Editors:
+# WinEdt
+*.bak
+*.sav
+
+# Texpad
+.texpadtmp
+
+# Kile
+*.backup
+
+# KBibTeX
+*~[0-9]*
diff --git a/vendor/gitignore/Terraform.gitignore b/vendor/gitignore/Terraform.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..7868d16d216f4aefc9a5ae335451af508629402f
--- /dev/null
+++ b/vendor/gitignore/Terraform.gitignore
@@ -0,0 +1,3 @@
+# Compiled files
+*.tfstate
+*.tfstate.backup
diff --git a/vendor/gitignore/Textpattern.gitignore b/vendor/gitignore/Textpattern.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..3805636d622db10fc13f283bcb0613336d7b6eca
--- /dev/null
+++ b/vendor/gitignore/Textpattern.gitignore
@@ -0,0 +1,11 @@
+.htaccess
+css.php
+rpc/
+sites/site*/admin/
+sites/site*/private/
+sites/site*/public/admin/
+sites/site*/public/setup/
+sites/site*/public/theme/
+textpattern/
+HISTORY.txt
+README.txt
diff --git a/vendor/gitignore/TurboGears2.gitignore b/vendor/gitignore/TurboGears2.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..122b3de221fee44327ae71f8610e96361db3bdc7
--- /dev/null
+++ b/vendor/gitignore/TurboGears2.gitignore
@@ -0,0 +1,20 @@
+*.py[co]
+
+# Default development database
+devdata.db
+
+# Default data directory
+data/*
+
+# Packages
+*.egg
+*.egg-info
+dist
+build
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
diff --git a/vendor/gitignore/Typo3.gitignore b/vendor/gitignore/Typo3.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..cb024fefe996b8894fb94b53f9d9657a64aa72a6
--- /dev/null
+++ b/vendor/gitignore/Typo3.gitignore
@@ -0,0 +1,20 @@
+## TYPO3 v6.2
+# Ignore several upload and file directories.
+/fileadmin/user_upload/
+/fileadmin/_temp_/
+/fileadmin/_processed_/
+/uploads/
+# Ignore cache
+/typo3conf/temp_CACHED*
+/typo3conf/temp_fieldInfo.php
+/typo3conf/deprecation_*.log
+/typo3conf/AdditionalConfiguration.php
+# Ignore system folders, you should have them symlinked.
+# If not comment out the following entries.
+/typo3
+/typo3_src
+/typo3_src-*
+/.htaccess
+/index.php
+# Ignore temp directory.
+/typo3temp/
diff --git a/vendor/gitignore/Umbraco.gitignore b/vendor/gitignore/Umbraco.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..ea05e1fb2a9e95c25d6e8ee4f62c61e5d7619e04
--- /dev/null
+++ b/vendor/gitignore/Umbraco.gitignore
@@ -0,0 +1,19 @@
+# Note: VisualStudio gitignore rules may also be relevant
+
+# Umbraco
+# Ignore unimportant folders generated by Umbraco
+**/App_Data/Logs/
+**/App_Data/[Pp]review/
+**/App_Data/TEMP/
+**/App_Data/NuGetBackup/
+
+# Ignore Umbraco content cache file
+**/App_Data/umbraco.config
+
+# Don't ignore Umbraco packages (VisualStudio.gitignore mistakes this for a NuGet packages folder)
+# Make sure to include details from VisualStudio.gitignore BEFORE this
+!**/App_Data/[Pp]ackages/
+!**/[Uu]mbraco/[Dd]eveloper/[Pp]ackages
+
+# ImageProcessor DiskCache 
+**/App_Data/cache/
diff --git a/vendor/gitignore/Unity.gitignore b/vendor/gitignore/Unity.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..5aafcbb7f1d4b3f02e1c63c1ce1693aa2b5a9cc8
--- /dev/null
+++ b/vendor/gitignore/Unity.gitignore
@@ -0,0 +1,30 @@
+/[Ll]ibrary/
+/[Tt]emp/
+/[Oo]bj/
+/[Bb]uild/
+/[Bb]uilds/
+/Assets/AssetStoreTools*
+
+# Autogenerated VS/MD solution and project files
+ExportedObj/
+*.csproj
+*.unityproj
+*.sln
+*.suo
+*.tmp
+*.user
+*.userprefs
+*.pidb
+*.booproj
+*.svd
+
+
+# Unity3D generated meta files
+*.pidb.meta
+
+# Unity3D Generated File On Crash Reports
+sysinfo.txt
+
+# Builds
+*.apk
+*.unitypackage
diff --git a/vendor/gitignore/UnrealEngine.gitignore b/vendor/gitignore/UnrealEngine.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..75b1186b0afd4292bf86accdcc1685b62f1a673d
--- /dev/null
+++ b/vendor/gitignore/UnrealEngine.gitignore
@@ -0,0 +1,62 @@
+# Visual Studio 2015 user specific files
+.vs/
+
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+*.ipa
+
+# These project files can be generated by the engine
+*.xcodeproj
+*.sln
+*.suo
+*.opensdf
+*.sdf
+*.VC.opendb
+
+# Precompiled Assets
+SourceArt/**/*.png
+SourceArt/**/*.tga
+
+# Binary Files
+Binaries/*
+
+# Builds
+Build/*
+
+# Don't ignore icon files in Build
+!Build/**/*.ico
+
+# Configuration files generated by the Editor
+Saved/*
+
+# Compiled source files for the engine to use
+Intermediate/*
+
+# Cache files for the editor to use
+DerivedDataCache/*
diff --git a/vendor/gitignore/VVVV.gitignore b/vendor/gitignore/VVVV.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..5df4324603e0cd5f096e56a3a669f962cbde4509
--- /dev/null
+++ b/vendor/gitignore/VVVV.gitignore
@@ -0,0 +1,6 @@
+
+# .v4p backup files
+*~.xml
+
+# Dynamic plugins .dll
+bin/
diff --git a/vendor/gitignore/VisualStudio.gitignore b/vendor/gitignore/VisualStudio.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..f1e3d20e056857d2f29dd50d10cbdd1e18ecee4c
--- /dev/null
+++ b/vendor/gitignore/VisualStudio.gitignore
@@ -0,0 +1,252 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# DNX
+project.lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignoreable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+node_modules/
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
diff --git a/vendor/gitignore/Waf.gitignore b/vendor/gitignore/Waf.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..48e8d8f7be4d6a0274474e36010e54eccacd5b40
--- /dev/null
+++ b/vendor/gitignore/Waf.gitignore
@@ -0,0 +1,4 @@
+# for projects that use Waf for building: http://code.google.com/p/waf/
+.waf-*
+.waf3-*
+.lock-*
diff --git a/vendor/gitignore/WordPress.gitignore b/vendor/gitignore/WordPress.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..97923503c4cb864a5d6d4c7f4b21ace73a9fdb51
--- /dev/null
+++ b/vendor/gitignore/WordPress.gitignore
@@ -0,0 +1,18 @@
+*.log
+wp-config.php
+wp-content/advanced-cache.php
+wp-content/backup-db/
+wp-content/backups/
+wp-content/blogs.dir/
+wp-content/cache/
+wp-content/upgrade/
+wp-content/uploads/
+wp-content/wp-cache-config.php
+wp-content/plugins/hello.php
+
+/.htaccess
+/license.txt
+/readme.html
+/sitemap.xml
+/sitemap.xml.gz
+
diff --git a/vendor/gitignore/Xojo.gitignore b/vendor/gitignore/Xojo.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..1b036dd4f2eb138d6b1facf724f9db5b1acdf3e9
--- /dev/null
+++ b/vendor/gitignore/Xojo.gitignore
@@ -0,0 +1,11 @@
+# Xojo (formerly REALbasic and Real Studio)
+
+Builds*
+*.debug
+*.debug.app
+Debug*.exe
+Debug*/Debug*.exe
+Debug*/Debug*\ Libs
+*.rbuistate
+*.xojo_uistate
+*.obsolete
diff --git a/vendor/gitignore/Yeoman.gitignore b/vendor/gitignore/Yeoman.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..7170d72018d19c0c6ce0fdb43c5f757cbe326c63
--- /dev/null
+++ b/vendor/gitignore/Yeoman.gitignore
@@ -0,0 +1,6 @@
+node_modules/
+bower_components/
+*.log
+
+build/
+dist/
diff --git a/vendor/gitignore/Yii.gitignore b/vendor/gitignore/Yii.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..70f087546f2c77ed7f0c921f4a1011ac765a0481
--- /dev/null
+++ b/vendor/gitignore/Yii.gitignore
@@ -0,0 +1,6 @@
+assets/*
+!assets/.gitignore
+protected/runtime/*
+!protected/runtime/.gitignore
+protected/data/*.db
+themes/classic/views/
diff --git a/vendor/gitignore/ZendFramework.gitignore b/vendor/gitignore/ZendFramework.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..80adb154900fd6a4d0c38c899a92455c4b821553
--- /dev/null
+++ b/vendor/gitignore/ZendFramework.gitignore
@@ -0,0 +1,25 @@
+# Composer files
+composer.phar
+vendor/
+
+# Local configs
+config/autoload/*.local.php
+
+# Binary gettext files
+*.mo
+
+# Data
+data/logs/
+data/cache/
+data/sessions/
+data/tmp/
+temp/
+
+#Doctrine 2
+data/DoctrineORMModule/Proxy/
+data/DoctrineORMModule/cache/
+
+
+# Legacy ZF1
+demos/
+extras/documentation
diff --git a/vendor/gitignore/Zephir.gitignore b/vendor/gitignore/Zephir.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..839cb5d707038d3942c268c69d6b2b86639ca33a
--- /dev/null
+++ b/vendor/gitignore/Zephir.gitignore
@@ -0,0 +1,26 @@
+# Cache files, generates by Zephir
+.temp/
+.libs/
+
+# Object files, generates by linker
+*.lo
+*.la
+*.o
+*.loT
+
+# Files generated by configure and Zephir,
+# not required for extension compilation.
+ext/build/
+ext/modules/
+ext/Makefile*
+ext/config*
+ext/acinclude.m4
+ext/aclocal.m4
+ext/autom4te*
+ext/install-sh
+ext/ltmain.sh
+ext/missing
+ext/mkinstalldirs
+ext/run-tests.php
+ext/.deps
+ext/libtool