diff --git a/.hound.yml b/.hound.yml
deleted file mode 100644
index 3bde29fb2bf974a6345220ff6d7ac4247e1e4621..0000000000000000000000000000000000000000
--- a/.hound.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-# Prefer single quotes
-StringLiterals:
-  EnforcedStyle: single_quotes
-  Enabled: true
diff --git a/.rubocop.yml b/.rubocop.yml
index 3aac8401848019e8189eab9383bec3099d7b2b19..db0bcfadcf4fe2f0b8d189f1e2ffa66560a2cea2 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -2,6 +2,8 @@ require:
   - rubocop-rspec
   - ./rubocop/rubocop
 
+inherit_from: .rubocop_todo.yml
+
 AllCops:
   TargetRubyVersion: 2.1
   # Cop names are not displayed in offense messages by default. Change behavior
@@ -52,14 +54,6 @@ Style/AlignArray:
 Style/AlignHash:
   Enabled: true
 
-# Align the parameters of a method call if they span more than one line.
-Style/AlignParameters:
-  Enabled: false
-
-# Use &&/|| instead of and/or.
-Style/AndOr:
-  Enabled: false
-
 # Use `Array#join` instead of `Array#*`.
 Style/ArrayJoin:
   Enabled: true
@@ -80,10 +74,6 @@ Style/Attr:
 Style/BeginBlock:
   Enabled: true
 
-# Checks if usage of %() or %Q() matches configuration.
-Style/BarePercentLiterals:
-  Enabled: false
-
 # Do not use block comments.
 Style/BlockComments:
   Enabled: true
@@ -97,14 +87,6 @@ Style/BlockEndNewline:
 Style/BlockDelimiters:
   Enabled: true
 
-# Enforce braces style around hash parameters.
-Style/BracesAroundHashParameters:
-  Enabled: false
-
-# Avoid explicit use of the case equality operator(===).
-Style/CaseEquality:
-  Enabled: false
-
 # Indentation of when in a case/when/[else/]end.
 Style/CaseIndentation:
   Enabled: true
@@ -133,24 +115,10 @@ Style/ClassMethods:
 Style/ClassVars:
   Enabled: true
 
-# Do not use :: for method call.
-Style/ColonMethodCall:
-  Enabled: false
-
-# Checks formatting of special comments (TODO, FIXME, OPTIMIZE, HACK, REVIEW).
-Style/CommentAnnotation:
-  Enabled: false
-
 # Indentation of comments.
 Style/CommentIndentation:
   Enabled: true
 
-# Use the return value of `if` and `case` statements for assignment to a
-# variable and variable comparison instead of assigning that variable
-# inside of each branch.
-Style/ConditionalAssignment:
-  Enabled: false
-
 # Constants should use SCREAMING_SNAKE_CASE.
 Style/ConstantName:
   Enabled: true
@@ -159,34 +127,14 @@ Style/ConstantName:
 Style/DefWithParentheses:
   Enabled: true
 
-# Checks for use of deprecated Hash methods.
-Style/DeprecatedHashMethods:
-  Enabled: false
-
 # Document classes and non-namespace modules.
 Style/Documentation:
   Enabled: false
 
-# Checks the position of the dot in multi-line method calls.
-Style/DotPosition:
-  Enabled: false
-
-# Checks for uses of double negation (!!).
-Style/DoubleNegation:
-  Enabled: false
-
-# Prefer `each_with_object` over `inject` or `reduce`.
-Style/EachWithObject:
-  Enabled: false
-
 # Align elses and elsifs correctly.
 Style/ElseAlignment:
   Enabled: true
 
-# Avoid empty else-clauses.
-Style/EmptyElse:
-  Enabled: false
-
 # Use empty lines between defs.
 Style/EmptyLineBetweenDefs:
   Enabled: false
@@ -215,10 +163,6 @@ Style/EmptyLinesAroundModuleBody:
 Style/EmptyLinesAroundMethodBody:
   Enabled: false
 
-# Prefer literals to Array.new/Hash.new/String.new.
-Style/EmptyLiteral:
-  Enabled: false
-
 # Avoid the use of END blocks.
 Style/EndBlock:
   Enabled: true
@@ -231,10 +175,6 @@ Style/EndOfLine:
 Style/EvenOdd:
   Enabled: true
 
-# Do not use unnecessary spacing.
-Style/ExtraSpacing:
-  Enabled: false
-
 # Use snake_case for source file names.
 Style/FileName:
   Enabled: true
@@ -252,31 +192,15 @@ Style/FlipFlop:
 Style/For:
   Enabled: true
 
-# Enforce the use of Kernel#sprintf, Kernel#format or String#%.
-Style/FormatString:
-  Enabled: false
-
 # Do not introduce global variables.
 Style/GlobalVars:
   Enabled: true
 
-# Check for conditionals that can be replaced with guard clauses.
-Style/GuardClause:
-  Enabled: false
-
 # Prefer Ruby 1.9 hash syntax `{ a: 1, b: 2 }`
 # over 1.8 syntax `{ :a => 1, :b => 2 }`.
 Style/HashSyntax:
   Enabled: true
 
-# Finds if nodes inside else, which can be converted to elsif.
-Style/IfInsideElse:
-  Enabled: false
-
-# Favor modifier if/unless usage when you have a single-line body.
-Style/IfUnlessModifier:
-  Enabled: false
-
 # Do not use if x; .... Use the ternary operator instead.
 Style/IfWithSemicolon:
   Enabled: true
@@ -299,22 +223,10 @@ Style/IndentationConsistency:
 Style/IndentationWidth:
   Enabled: true
 
-# Checks the indentation of the first element in an array literal.
-Style/IndentArray:
-  Enabled: false
-
-# Checks the indentation of the first key in a hash literal.
-Style/IndentHash:
-  Enabled: false
-
 # Use Kernel#loop for infinite loops.
 Style/InfiniteLoop:
   Enabled: true
 
-# Use the new lambda literal syntax for single-line blocks.
-Style/Lambda:
-  Enabled: false
-
 # Use lambda.call(...) instead of lambda.(...).
 Style/LambdaCall:
   Enabled: true
@@ -323,14 +235,6 @@ Style/LambdaCall:
 Style/LeadingCommentSpace:
   Enabled: true
 
-# Use \ instead of + or << to concatenate two string literals at line end.
-Style/LineEndConcatenation:
-  Enabled: false
-
-# Do not use parentheses for method calls with no arguments.
-Style/MethodCallParentheses:
-  Enabled: false
-
 # Checks if the method definitions have or don't have parentheses.
 Style/MethodDefParentheses:
   Enabled: true
@@ -387,39 +291,18 @@ Style/MultilineMethodDefinitionBraceLayout:
 Style/MultilineOperationIndentation:
   Enabled: false
 
-# Avoid multi-line `? :` (the ternary operator), use if/unless instead.
-Style/MultilineTernaryOperator:
-  Enabled: false
-
-# Do not assign mutable objects to constants.
-Style/MutableConstant:
-  Enabled: false
-
 # Favor unless over if for negative conditions (or control flow or).
 Style/NegatedIf:
   Enabled: true
 
-# Favor until over while for negative conditions.
-Style/NegatedWhile:
-  Enabled: false
-
 # Avoid using nested modifiers.
 Style/NestedModifier:
   Enabled: true
 
-# Parenthesize method calls which are nested inside the argument list of
-# another parenthesized method call.
-Style/NestedParenthesizedCalls:
-  Enabled: false
-
 # Use one expression per branch in a ternary operator.
 Style/NestedTernaryOperator:
   Enabled: true
 
-# Use `next` to skip iteration instead of a condition at the end.
-Style/Next:
-  Enabled: false
-
 # Prefer x.nil? to x == nil.
 Style/NilComparison:
   Enabled: true
@@ -444,51 +327,10 @@ Style/OneLineConditional:
 Style/OpMethod:
   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.
-Style/ParallelAssignment:
-  Enabled: false
-
 # Don't use parentheses around the condition of an if/unless/while.
 Style/ParenthesesAroundCondition:
   Enabled: true
 
-# Use `%`-literal delimiters consistently.
-Style/PercentLiteralDelimiters:
-  Enabled: false
-
-# Checks if uses of %Q/%q match the configured preference.
-Style/PercentQLiterals:
-  Enabled: false
-
-# Avoid Perl-style regex back references.
-Style/PerlBackrefs:
-  Enabled: false
-
-# Check the names of predicate methods.
-Style/PredicateName:
-  Enabled: false
-
-# Use proc instead of Proc.new.
-Style/Proc:
-  Enabled: false
-
-# Checks the arguments passed to raise/fail.
-Style/RaiseArgs:
-  Enabled: false
-
-# Don't use begin blocks when they are not needed.
-Style/RedundantBegin:
-  Enabled: false
-
-# Checks for an obsolete RuntimeException argument in raise/fail.
-Style/RedundantException:
-  Enabled: false
-
-# Checks usages of Object#freeze on immutable objects.
-Style/RedundantFreeze:
-  Enabled: false
-
 # Checks for parentheses that seem not to serve any purpose.
 Style/RedundantParentheses:
   Enabled: true
@@ -497,24 +339,6 @@ Style/RedundantParentheses:
 Style/RedundantReturn:
   Enabled: true
 
-# Don't use self where it's not needed.
-Style/RedundantSelf:
-  Enabled: false
-
-# Use %r for regular expressions matching more than `MaxSlashes` '/'
-# characters. Use %r only for regular expressions matching more
-# than `MaxSlashes` '/' character.
-Style/RegexpLiteral:
-  Enabled: false
-
-# Avoid using rescue in its modifier form.
-Style/RescueModifier:
-  Enabled: false
-
-# Checks for places where self-assignment shorthand should have been used.
-Style/SelfAssignment:
-  Enabled: false
-
 # Don't use semicolons to terminate expressions.
 Style/Semicolon:
   Enabled: true
@@ -524,14 +348,6 @@ Style/SignalException:
   EnforcedStyle: only_raise
   Enabled: true
 
-# Enforces the names of some block params.
-Style/SingleLineBlockParams:
-  Enabled: false
-
-# Avoid single-line methods.
-Style/SingleLineMethods:
-  Enabled: false
-
 # Use spaces after colons.
 Style/SpaceAfterColon:
   Enabled: true
@@ -553,11 +369,6 @@ Style/SpaceAfterNot:
 Style/SpaceAfterSemicolon:
   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
-
 # Use a space around keywords if appropriate.
 Style/SpaceAroundKeyword:
   Enabled: true
@@ -566,10 +377,6 @@ Style/SpaceAroundKeyword:
 Style/SpaceAroundOperators:
   Enabled: true
 
-# Checks that the left block brace has or doesn't have space before it.
-Style/SpaceBeforeBlockBraces:
-  Enabled: false
-
 # No spaces before commas.
 Style/SpaceBeforeComma:
   Enabled: true
@@ -578,33 +385,14 @@ Style/SpaceBeforeComma:
 Style/SpaceBeforeComment:
   Enabled: true
 
-# Checks that exactly one space is used between a method name and the first
-# argument for method calls without parentheses.
-Style/SpaceBeforeFirstArg:
-  Enabled: false
-
 # No spaces before semicolons.
 Style/SpaceBeforeSemicolon:
   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
-# have trailing space.
-Style/SpaceInsideBlockBraces:
-  Enabled: false
-
-# No spaces after [ or before ].
-Style/SpaceInsideBrackets:
-  Enabled: false
-
 # Use spaces inside hash literal braces - or don't.
 Style/SpaceInsideHashLiteralBraces:
   Enabled: true
 
-# No spaces after ( or before ).
-Style/SpaceInsideParens:
-  Enabled: false
-
 # No spaces inside range literals.
 Style/SpaceInsideRangeLiteral:
   Enabled: true
@@ -614,10 +402,6 @@ Style/SpaceInsideStringInterpolation:
   EnforcedStyle: no_space
   Enabled: true
 
-# Avoid Perl-style global variables.
-Style/SpecialGlobalVars:
-  Enabled: false
-
 # Check for the usage of parentheses around stabby lambda arguments.
 Style/StabbyLambdaParentheses:
   EnforcedStyle: require_parentheses
@@ -627,25 +411,12 @@ Style/StabbyLambdaParentheses:
 Style/StringLiterals:
   Enabled: false
 
-# Checks if uses of quotes inside expressions in interpolated strings match the
-# configured preference.
-Style/StringLiteralsInInterpolation:
-  Enabled: false
-
 # Checks if configured preferred methods are used over non-preferred.
 Style/StringMethods:
   PreferredMethods:
     intern: to_sym
   Enabled: true
 
-# Use %i or %I for arrays of symbols.
-Style/SymbolArray:
-  Enabled: false
-
-# Use symbols as procs instead of blocks when possible.
-Style/SymbolProc:
-  Enabled: false
-
 # No hard tabs.
 Style/Tab:
   Enabled: true
@@ -654,40 +425,10 @@ Style/Tab:
 Style/TrailingBlankLines:
   Enabled: true
 
-# Checks for trailing comma in array and hash literals.
-Style/TrailingCommaInLiteral:
-  Enabled: false
-
-# Checks for trailing comma in argument lists.
-Style/TrailingCommaInArguments:
-  Enabled: false
-
-# Avoid trailing whitespace.
-Style/TrailingWhitespace:
-  Enabled: false
-
-# Checks for the usage of unneeded trailing underscores at the end of
-# parallel variable assignment.
-Style/TrailingUnderscoreVariable:
-  Enabled: false
-
-# Prefer attr_* methods to trivial readers/writers.
-Style/TrivialAccessors:
-  Enabled: false
-
-# Do not use unless with else. Rewrite these with the positive case first.
-Style/UnlessElse:
-  Enabled: false
-
 # Checks for %W when interpolation is not needed.
 Style/UnneededCapitalW:
   Enabled: true
 
-# TODO: Enable UnneededInterpolation Cop.
-# Checks for strings that are just an interpolated expression.
-Style/UnneededInterpolation:
-  Enabled: false
-
 # Checks for %q/%Q when single quotes or double quotes would do.
 Style/UnneededPercentQ:
   Enabled: false
@@ -717,12 +458,6 @@ Style/WhileUntilModifier:
 Style/WordArray:
   Enabled: false
 
-# TODO: Enable ZeroLengthPredicate Cop.
-# Use #empty? when testing for objects of length 0.
-Style/ZeroLengthPredicate:
-  Enabled: false
-
-
 #################### Metrics ################################
 
 # A calculated magnitude based on number of assignments,
@@ -776,15 +511,6 @@ Metrics/PerceivedComplexity:
 Lint/AmbiguousOperator:
   Enabled: true
 
-# Checks for ambiguous regexp literals in the first argument of a method
-# invocation without parentheses.
-Lint/AmbiguousRegexpLiteral:
-  Enabled: false
-
-# Don't use assignment in conditions.
-Lint/AssignmentInCondition:
-  Enabled: false
-
 # Align block ends correctly.
 Lint/BlockAlignment:
   Enabled: true
@@ -810,14 +536,6 @@ Lint/DefEndAlignment:
 Lint/DeprecatedClassMethods:
   Enabled: true
 
-# Check for duplicate method definitions.
-Lint/DuplicateMethods:
-  Enabled: false
-
-# Check for duplicate keys in hash literals.
-Lint/DuplicatedKey:
-  Enabled: false
-
 # Check for immutable argument given to each_with_object.
 Lint/EachWithObjectArgument:
   Enabled: true
@@ -830,10 +548,6 @@ Lint/ElseLayout:
 Lint/EmptyEnsure:
   Enabled: true
 
-# Checks for empty string interpolation.
-Lint/EmptyInterpolation:
-  Enabled: false
-
 # Align ends correctly.
 Lint/EndAlignment:
   Enabled: true
@@ -858,21 +572,11 @@ Lint/FloatOutOfRange:
 Lint/FormatParameterMismatch:
   Enabled: true
 
-# Don't suppress exception.
-Lint/HandleExceptions:
-  Enabled: false
-
 # Checks for adjacent string literals on the same line, which could better be
 # represented as a single string literal.
 Lint/ImplicitStringConcatenation:
   Enabled: true
 
-# TODO: Enable IneffectiveAccessModifier Cop.
-# Checks for attempts to use `private` or `protected` to set the visibility
-# of a class method, which does not work.
-Lint/IneffectiveAccessModifier:
-  Enabled: false
-
 # Checks for invalid character literals with a non-escaped whitespace
 # character.
 Lint/InvalidCharacterLiteral:
@@ -886,11 +590,6 @@ Lint/LiteralInCondition:
 Lint/LiteralInInterpolation:
   Enabled: true
 
-# Use Kernel#loop with break rather than begin/end/until or begin/end/while
-# for post-loop tests.
-Lint/Loop:
-  Enabled: false
-
 # Do not use nested method definitions.
 Lint/NestedMethodDefinition:
   Enabled: true
@@ -916,13 +615,8 @@ Lint/RequireParentheses:
 Lint/RescueException:
   Enabled: true
 
-# Do not use the same name as outer local variable for block arguments
-# or block local variables.
-Lint/ShadowingOuterLocalVariable:
-  Enabled: false
-
-# 'Checks for Object#to_s usage in string interpolation.
-Lint/StringConversionInInterpolation:
+# Checks for the order which exceptions are rescued to avoid rescueing a less specific exception before a more specific exception.
+Lint/ShadowedException:
   Enabled: false
 
 # Do not use prefix `_` for a variable that is used.
@@ -935,22 +629,10 @@ Lint/UnderscorePrefixedVariableName:
 Lint/UnneededDisable:
   Enabled: false
 
-# Checks for unused block arguments.
-Lint/UnusedBlockArgument:
-  Enabled: false
-
-# Checks for unused method arguments.
-Lint/UnusedMethodArgument:
-  Enabled: false
-
 # Unreachable code.
 Lint/UnreachableCode:
   Enabled: true
 
-# Checks for useless access modifiers.
-Lint/UselessAccessModifier:
-  Enabled: false
-
 # Checks for useless assignment to a local variable.
 Lint/UselessAssignment:
   Enabled: true
@@ -983,11 +665,6 @@ Performance/Casecmp:
 Performance/DoubleStartEndWith:
   Enabled: true
 
-# TODO: Enable EndWith Cop.
-# Use `end_with?` instead of a regex match anchored to the end of a string.
-Performance/EndWith:
-  Enabled: false
-
 # Use `strip` instead of `lstrip.rstrip`.
 Performance/LstripRstrip:
   Enabled: true
@@ -996,24 +673,6 @@ Performance/LstripRstrip:
 Performance/RangeInclude:
   Enabled: true
 
-# TODO: Enable RedundantBlockCall Cop.
-# Use `yield` instead of `block.call`.
-Performance/RedundantBlockCall:
-  Enabled: false
-
-# TODO: Enable RedundantMatch Cop.
-# Use `=~` instead of `String#match` or `Regexp#match` in a context where the
-# returned `MatchData` is not needed.
-Performance/RedundantMatch:
-  Enabled: false
-
-# TODO: Enable RedundantMerge Cop.
-# Use `Hash#[]=`, rather than `Hash#merge!` with a single key-value pair.
-Performance/RedundantMerge:
-  # Max number of key-value pairs to consider an offense
-  MaxKeyValuePairs: 2
-  Enabled: false
-
 # Use `sort` instead of `sort_by { |x| x }`.
 Performance/RedundantSortBy:
   Enabled: true
@@ -1082,18 +741,6 @@ Rails/ReadWriteAttribute:
 Rails/ScopeArgs:
   Enabled: true
 
-# Checks the correct usage of time zone aware methods.
-# http://danilenko.org/2012/7/6/rails_timezones
-Rails/TimeZone:
-  Enabled: false
-
-# Use validates :attribute, hash of validations.
-Rails/Validation:
-  Enabled: false
-
-Rails/UniqBeforePluck:
-  Enabled: false
-
 ##################### RSpec ##################################
 
 # Check that instances are not being stubbed globally.
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9310e71188978e5aa3578096fe61f53a4fd3dff9
--- /dev/null
+++ b/.rubocop_todo.yml
@@ -0,0 +1,462 @@
+# This configuration was generated by
+# `rubocop --auto-gen-config --exclude-limit 0`
+# on 2016-07-13 12:36:08 -0600 using RuboCop version 0.41.2.
+# The point is for the user to remove these configuration records
+# one by one as the offenses are removed from the code base.
+# Note that changes in the inspected code, or installation of new
+# versions of RuboCop, may require this file to be generated again.
+
+# Offense count: 154
+Lint/AmbiguousRegexpLiteral:
+  Enabled: false
+
+# Offense count: 43
+# Configuration parameters: AllowSafeAssignment.
+Lint/AssignmentInCondition:
+  Enabled: false
+
+# Offense count: 14
+Lint/HandleExceptions:
+  Enabled: false
+
+# Offense count: 21
+Lint/IneffectiveAccessModifier:
+  Enabled: false
+
+# Offense count: 2
+Lint/Loop:
+  Enabled: false
+
+# Offense count: 15
+Lint/ShadowingOuterLocalVariable:
+  Enabled: false
+
+# Offense count: 3
+# Cop supports --auto-correct.
+Lint/StringConversionInInterpolation:
+  Enabled: false
+
+# Offense count: 44
+# Cop supports --auto-correct.
+# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
+Lint/UnusedBlockArgument:
+  Enabled: false
+
+# Offense count: 129
+# Cop supports --auto-correct.
+# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods.
+Lint/UnusedMethodArgument:
+  Enabled: false
+
+# Offense count: 11
+Lint/UselessAccessModifier:
+  Enabled: false
+
+# Offense count: 12
+# Cop supports --auto-correct.
+Performance/PushSplat:
+  Enabled: false
+
+# Offense count: 2
+# Cop supports --auto-correct.
+Performance/RedundantBlockCall:
+  Enabled: false
+
+# Offense count: 4
+# Cop supports --auto-correct.
+Performance/RedundantMatch:
+  Enabled: false
+
+# Offense count: 24
+# Cop supports --auto-correct.
+# Configuration parameters: MaxKeyValuePairs.
+Performance/RedundantMerge:
+  Enabled: false
+
+# Offense count: 60
+Rails/OutputSafety:
+  Enabled: false
+
+# Offense count: 128
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: strict, flexible
+Rails/TimeZone:
+  Enabled: false
+
+# Offense count: 12
+# Cop supports --auto-correct.
+# Configuration parameters: Include.
+# Include: app/models/**/*.rb
+Rails/Validation:
+  Enabled: false
+
+# Offense count: 217
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
+# SupportedStyles: with_first_parameter, with_fixed_indentation
+Style/AlignParameters:
+  Enabled: false
+
+# Offense count: 32
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: always, conditionals
+Style/AndOr:
+  Enabled: false
+
+# Offense count: 47
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: percent_q, bare_percent
+Style/BarePercentLiterals:
+  Enabled: false
+
+# Offense count: 258
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: braces, no_braces, context_dependent
+Style/BracesAroundHashParameters:
+  Enabled: false
+
+# Offense count: 5
+Style/CaseEquality:
+  Enabled: false
+
+# Offense count: 19
+# Cop supports --auto-correct.
+Style/ColonMethodCall:
+  Enabled: false
+
+# Offense count: 3
+# Cop supports --auto-correct.
+# Configuration parameters: Keywords.
+# Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW
+Style/CommentAnnotation:
+  Enabled: false
+
+# Offense count: 34
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, SingleLineConditionsOnly.
+# SupportedStyles: assign_to_condition, assign_inside_condition
+Style/ConditionalAssignment:
+  Enabled: false
+
+# Offense count: 789
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: leading, trailing
+Style/DotPosition:
+  Enabled: false
+
+# Offense count: 13
+Style/DoubleNegation:
+  Enabled: false
+
+# Offense count: 3
+Style/EachWithObject:
+  Enabled: false
+
+# Offense count: 30
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: empty, nil, both
+Style/EmptyElse:
+  Enabled: false
+
+# Offense count: 3
+# Cop supports --auto-correct.
+Style/EmptyLiteral:
+  Enabled: false
+
+# Offense count: 123
+# Cop supports --auto-correct.
+# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment.
+Style/ExtraSpacing:
+  Enabled: false
+
+# Offense count: 7
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: format, sprintf, percent
+Style/FormatString:
+  Enabled: false
+
+# Offense count: 48
+# Configuration parameters: MinBodyLength.
+Style/GuardClause:
+  Enabled: false
+
+# Offense count: 11
+Style/IfInsideElse:
+  Enabled: false
+
+# Offense count: 177
+# Cop supports --auto-correct.
+# Configuration parameters: MaxLineLength.
+Style/IfUnlessModifier:
+  Enabled: false
+
+# Offense count: 52
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
+# SupportedStyles: special_inside_parentheses, consistent, align_brackets
+Style/IndentArray:
+  Enabled: false
+
+# Offense count: 89
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
+# SupportedStyles: special_inside_parentheses, consistent, align_braces
+Style/IndentHash:
+  Enabled: false
+
+# Offense count: 12
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: line_count_dependent, lambda, literal
+Style/Lambda:
+  Enabled: false
+
+# Offense count: 6
+# Cop supports --auto-correct.
+Style/LineEndConcatenation:
+  Enabled: false
+
+# Offense count: 13
+# Cop supports --auto-correct.
+Style/MethodCallParentheses:
+  Enabled: false
+
+# Offense count: 3
+Style/MultilineTernaryOperator:
+  Enabled: false
+
+# Offense count: 62
+# Cop supports --auto-correct.
+Style/MutableConstant:
+  Enabled: false
+
+# Offense count: 10
+# Cop supports --auto-correct.
+Style/NestedParenthesizedCalls:
+  Enabled: false
+
+# Offense count: 12
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles.
+# SupportedStyles: skip_modifier_ifs, always
+Style/Next:
+  Enabled: false
+
+# Offense count: 8
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedOctalStyle, SupportedOctalStyles.
+# SupportedOctalStyles: zero_with_o, zero_only
+Style/NumericLiteralPrefix:
+  Enabled: false
+
+# Offense count: 29
+# Cop supports --auto-correct.
+Style/ParallelAssignment:
+  Enabled: false
+
+# Offense count: 208
+# Cop supports --auto-correct.
+# Configuration parameters: PreferredDelimiters.
+Style/PercentLiteralDelimiters:
+  Enabled: false
+
+# Offense count: 11
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: lower_case_q, upper_case_q
+Style/PercentQLiterals:
+  Enabled: false
+
+# Offense count: 13
+# Cop supports --auto-correct.
+Style/PerlBackrefs:
+  Enabled: false
+
+# Offense count: 32
+# Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist.
+# NamePrefix: is_, has_, have_
+# NamePrefixBlacklist: is_, has_, have_
+# NameWhitelist: is_a?
+Style/PredicateName:
+  Enabled: false
+
+# Offense count: 28
+# Cop supports --auto-correct.
+Style/PreferredHashMethods:
+  Enabled: false
+
+# Offense count: 6
+# Cop supports --auto-correct.
+Style/Proc:
+  Enabled: false
+
+# Offense count: 20
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: compact, exploded
+Style/RaiseArgs:
+  Enabled: false
+
+# Offense count: 3
+# Cop supports --auto-correct.
+Style/RedundantBegin:
+  Enabled: false
+
+# Offense count: 1
+# Cop supports --auto-correct.
+Style/RedundantException:
+  Enabled: false
+
+# Offense count: 23
+# Cop supports --auto-correct.
+Style/RedundantFreeze:
+  Enabled: false
+
+# Offense count: 377
+# Cop supports --auto-correct.
+Style/RedundantSelf:
+  Enabled: false
+
+# Offense count: 94
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes.
+# SupportedStyles: slashes, percent_r, mixed
+Style/RegexpLiteral:
+  Enabled: false
+
+# Offense count: 17
+# Cop supports --auto-correct.
+Style/RescueModifier:
+  Enabled: false
+
+# Offense count: 2
+# Cop supports --auto-correct.
+Style/SelfAssignment:
+  Enabled: false
+
+# Offense count: 2
+# Configuration parameters: Methods.
+# Methods: {"reduce"=>["a", "e"]}, {"inject"=>["a", "e"]}
+Style/SingleLineBlockParams:
+  Enabled: false
+
+# Offense count: 50
+# Cop supports --auto-correct.
+# Configuration parameters: AllowIfMethodIsEmpty.
+Style/SingleLineMethods:
+  Enabled: false
+
+# Offense count: 14
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: space, no_space
+Style/SpaceAroundEqualsInParameterDefault:
+  Enabled: false
+
+# Offense count: 119
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: space, no_space
+Style/SpaceBeforeBlockBraces:
+  Enabled: false
+
+# Offense count: 11
+# Cop supports --auto-correct.
+# Configuration parameters: AllowForAlignment.
+Style/SpaceBeforeFirstArg:
+  Enabled: false
+
+# Offense count: 130
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
+# SupportedStyles: space, no_space
+Style/SpaceInsideBlockBraces:
+  Enabled: false
+
+# Offense count: 98
+# Cop supports --auto-correct.
+Style/SpaceInsideBrackets:
+  Enabled: false
+
+# Offense count: 60
+# Cop supports --auto-correct.
+Style/SpaceInsideParens:
+  Enabled: false
+
+# Offense count: 5
+# Cop supports --auto-correct.
+Style/SpaceInsidePercentLiteralDelimiters:
+  Enabled: false
+
+# Offense count: 36
+# Cop supports --auto-correct.
+# Configuration parameters: SupportedStyles.
+# SupportedStyles: use_perl_names, use_english_names
+Style/SpecialGlobalVars:
+  EnforcedStyle: use_perl_names
+
+# Offense count: 30
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: single_quotes, double_quotes
+Style/StringLiteralsInInterpolation:
+  Enabled: false
+
+# Offense count: 24
+# Cop supports --auto-correct.
+# Configuration parameters: IgnoredMethods.
+# IgnoredMethods: respond_to, define_method
+Style/SymbolProc:
+  Enabled: false
+
+# Offense count: 23
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
+# SupportedStyles: comma, consistent_comma, no_comma
+Style/TrailingCommaInArguments:
+  Enabled: false
+
+# Offense count: 113
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
+# SupportedStyles: comma, consistent_comma, no_comma
+Style/TrailingCommaInLiteral:
+  Enabled: false
+
+# Offense count: 7
+# Cop supports --auto-correct.
+# Configuration parameters: AllowNamedUnderscoreVariables.
+Style/TrailingUnderscoreVariable:
+  Enabled: false
+
+# Offense count: 90
+# Cop supports --auto-correct.
+Style/TrailingWhitespace:
+  Enabled: false
+
+# Offense count: 2
+# Cop supports --auto-correct.
+# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, Whitelist.
+# Whitelist: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym
+Style/TrivialAccessors:
+  Enabled: false
+
+# Offense count: 3
+# Cop supports --auto-correct.
+Style/UnlessElse:
+  Enabled: false
+
+# Offense count: 13
+# Cop supports --auto-correct.
+Style/UnneededInterpolation:
+  Enabled: false
+
+# Offense count: 8
+# Cop supports --auto-correct.
+Style/ZeroLengthPredicate:
+  Enabled: false
diff --git a/.teatro.yml b/.teatro.yml
deleted file mode 100644
index 3005436198154fbc3f812f0b51a26500d37391b7..0000000000000000000000000000000000000000
--- a/.teatro.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-stage:
-  before:
-    - cp config/gitlab.teatro.yml config/gitlab.yml
-    - mkdir /apps/gitlab-satellites
-    - mkdir /apps/repositories
-    
-  database:
-    - RAILS_ENV=development force=yes bundle exec rake db:create gitlab:setup
\ No newline at end of file
diff --git a/CHANGELOG b/CHANGELOG
index a977fc3fdbfa52db04952facc06f6d67cc629e7c..fcfc2d7aa30e9524cb995e275f5d5e540e2ee3f5 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,51 +1,84 @@
 Please view this file on the master branch, on stable branches it's out of date.
 
 v 8.10.0 (unreleased)
+  - Expose {should,force}_remove_source_branch (Ben Boeckel)
+  - Disable PostgreSQL statement timeout during migrations
+  - Fix projects dropdown loading performance with a simplified api cal. !5113 (tiagonbotelho)
   - Fix commit builds API, return all builds for all pipelines for given commit. !4849
   - Replace Haml with Hamlit to make view rendering faster. !3666
+  - Refresh the branch cache after `git gc` runs
   - Refactor repository paths handling to allow multiple git mount points
+  - Optimize system note visibility checking by memoizing the visible reference count !5070
   - Add Application Setting to configure default Repository Path for new projects
+  - Delete award emoji when deleting a user
+  - Remove pinTo from Flash and make inline flash messages look nicer !4854 (winniehell)
   - Wrap code blocks on Activies and Todos page. !4783 (winniehell)
   - Align flash messages with left side of page content !4959 (winniehell)
+  - Display tooltip for "Copy to Clipboard" button !5164 (winniehell)
+  - Use default cursor for table header of project files !5165 (winniehell)
+  - Store when and yaml variables in builds table
   - Display last commit of deleted branch in push events !4699 (winniehell)
   - Escape file extension when parsing search results !5141 (winniehell)
   - Apply the trusted_proxies config to the rack request object for use with rack_attack
+  - Upgrade to Rails 4.2.7. !5236
   - Add Sidekiq queue duration to transaction metrics.
   - Add a new column `artifacts_size` to table `ci_builds` !4964
   - Let Workhorse serve format-patch diffs
+  - Display tooltip for mentioned users and groups !5261 (winniehell)
+  - Added day name to contribution calendar tooltips
   - Make images fit to the size of the viewport !4810
   - Fix check for New Branch button on Issue page !4630 (winniehell)
   - Fix MR-auto-close text added to description. !4836
+  - Support U2F devices in Firefox. !5177
   - Fix issue, preventing users w/o push access to sort tags !5105 (redetection)
   - Add Spring EmojiOne updates.
+  - Fix fetching LFS objects for private CI projects
   - Add syntax for multiline blockquote using `>>>` fence !3954
   - Fix viewing notification settings when a project is pending deletion
+  - Updated compare dropdown menus to use GL dropdown
+  - Eager load award emoji on notes
   - Fix pagination when sorting by columns with lots of ties (like priority)
   - The Markdown reference parsers now re-use query results to prevent running the same queries multiple times !5020
   - Updated project header design
+  - Issuable collapsed assignee tooltip is now the users name
   - Exclude email check from the standard health check
   - Updated layout for Projects, Groups, Users on Admin area !4424
   - Fix changing issue state columns in milestone view
+  - Update health_check gem to version 2.1.0
   - Add notification settings dropdown for groups
+  - Render inline diffs for multiple changed lines following eachother
   - Wildcards for protected branches. !4665
   - Allow importing from Github using Personal Access Tokens. (Eric K Idema)
+  - API: Expose `due_date` for issues (Robert Schilling)
   - API: Todos !3188 (Robert Schilling)
   - API: Expose shared groups for projects and shared projects for groups !5050 (Robert Schilling)
   - Add "Enabled Git access protocols" to Application Settings
+  - Diffs will create button/diff form on demand no on server side
+  - Reduce size of HTML used by diff comment forms
   - Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
   - Only show New Snippet button to users that can create snippets.
   - PipelinesFinder uses git cache data
+  - Track a user who created a pipeline
+  - Actually render old and new sections of parallel diff next to each other
   - Throttle the update of `project.pushes_since_gc` to 1 minute.
+  - Allow expanding and collapsing files in diff view (!4990)
+  - Collapse large diffs by default (!4990)
+  - Fix mentioned users list on diff notes
+  - Fix creation of deployment on build that is retried, redeployed or rollback
   - Check for conflicts with existing Project's wiki path when creating a new project.
   - Show last push widget in upstream after push to fork
+  - Fix stage status shown for pipelines
+  - Cache todos pending/done dashboard query counts.
   - Don't instantiate a git tree on Projects show default view
   - Bump Rinku to 2.0.0
   - Remove unused front-end variable -> default_issues_tracker
+  - ObjectRenderer retrieve renderer content using Rails.cache.read_multi
   - Better caching of git calls on ProjectsController#show.
   - Avoid to retrieve MR closes_issues as much as possible.
   - Add API endpoint for a group issues !4520 (mahcsig)
   - Add Bugzilla integration !4930 (iamtjg)
   - Instrument Rinku usage
+  - Be explicit to define merge request discussion variables
   - Metrics for Rouge::Plugins::Redcarpet and Rouge::Formatters::HTMLGitlab
   - RailsCache metris now includes fetch_hit/fetch_miss and read_hit/read_miss info.
   - Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w)
@@ -55,14 +88,47 @@ v 8.10.0 (unreleased)
   - Add basic system information like memory and disk usage to the admin panel
   - Don't garbage collect commits that have related DB records like comments
   - More descriptive message for git hooks and file locks
+  - Aliases of award emoji should be stored as original name. !5060 (dixpac)
   - Handle custom Git hook result in GitLab UI
+  - Allow to access Container Registry for Public and Internal projects
   - Allow '?', or '&' for label names
+  - Support redirected blobs for Container Registry integration
   - Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests
   - Add date when user joined the team on the member page
   - Fix 404 redirect after validation fails importing a GitLab project
   - Added setting to set new users by default as external !4545 (Dravere)
   - Add min value for project limit field on user's form !3622 (jastkand)
+  - Reset project pushes_since_gc when we enqueue the git gc call
   - Add reminder to not paste private SSH keys !4399 (Ingo Blechschmidt)
+  - Remove duplicate `description` field in `MergeRequest` entities (Ben Boeckel)
+  - Style of import project buttons were fixed in the new project page. !5183 (rdemirbay)
+  - Fix GitHub client requests when rate limit is disabled
+  - Optimistic locking for Issues and Merge Requests (Title and description overriding prevention)
+  - Redesign Builds and Pipelines pages
+  - Change status color and icon for running builds
+  - Fix markdown rendering for: consecutive labels references, label references that begin with a digit or contains `.`
+  - Project export filename now includes the project and namespace path
+  - Fix last update timestamp on issues not preserved on gitlab.com and project imports
+  - Fix issues importing projects from EE to CE
+  - Fix creating group with space in group path
+  - Create Todos for Issue author when assign or mention himself (Katarzyna Kobierska)
+  - Limit the number of retries on error to 3 for exporting projects
+  - Allow empty repositories on project import/export
+
+v 8.9.6
+  - Fix importing of events under notes for GitLab projects. !5154
+  - Fix log statements in import/export. !5129
+  - Fix commit avatar alignment in compare view. !5128
+  - Fix broken migration in MySQL. !5005
+  - Overwrite Host and X-Forwarded-Host headers in NGINX !5213
+  - Keeps issue number when importing from Gitlab.com
+  - Add Pending tab for Builds (Katarzyna Kobierska, Urszula Budziszewska)
+
+v 8.9.7 (unreleased)
+  - Fix import_data wrongly saved as a result of an invalid import_url
+
+v 8.9.6
+  - Fix importing of events under notes for GitLab projects
 
 v 8.9.5
   - Add more debug info to import/export and memory killer. !5108
@@ -223,6 +289,7 @@ v 8.9.0
   - 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
+  - Added shortcut to focus filter search fields and added documentation #18120
   - 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
@@ -2184,8 +2251,6 @@ v 7.7.0
   - Fixes for edit comments: drag-n-drop images, preview mode, selecting images, save & update
   - Remove password strength indicator
 
-
-
 v 7.6.0
   - Fork repository to groups
   - New rugged version
@@ -2611,13 +2676,13 @@ v 6.5.0
   - Files API supports base64 encoded content (sponsored by O'Reilly Media)
   - Added support for Go's repository retrieval (Bruno Albuquerque)
 
-v6.4.3
+v 6.4.3
   - Don't use unicorn worker killer if PhusionPassenger is defined
 
-v6.4.2
+v 6.4.2
   - Fixed wrong behaviour of script/upgrade.rb
 
-v6.4.1
+v 6.4.1
   - Fixed bug with repository rename
   - Fixed bug with project transfer
 
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f4472214778e6efcb8614145e4cbd6dc283331da..14ff05c9aa3b979d70bd201ae855464170b91f8e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -145,7 +145,8 @@ might be edited to make them small and simple.
 You are encouraged to use the template below for feature proposals.
 
 ```
-## Description including problem, use cases, benefits, and/or goals
+## Description
+Include problem, use cases, benefits, and/or goals
 
 ## Proposal
 
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index 879be8a98fc7a080771e966eb405fca1b40108de..e7c7d3cc3c89ada8384d34fee65534801993b979 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-0.7.7
+0.7.8
diff --git a/Gemfile b/Gemfile
index f1fef4caf7643a9a92eb07b81d2372ed79f1840f..81e8ff60ad5283a5b28c55b737dff792c89ec7e1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,6 @@
 source 'https://rubygems.org'
 
-gem 'rails', '4.2.6'
+gem 'rails', '4.2.7'
 gem 'rails-deprecated_sanitizer', '~> 1.0.3'
 
 # Responders respond_to and respond_with
@@ -61,7 +61,7 @@ gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: 'omniauth-ldap'
 
 # Git Wiki
 # Required manually in config/initializers/gollum.rb to control load order
-gem 'gollum-lib', '~> 4.1.0', require: false
+gem 'gollum-lib', '~> 4.2', require: false
 gem 'gollum-rugged_adapter', '~> 0.4.2', require: false
 
 # Language detection
@@ -105,7 +105,7 @@ gem 'seed-fu', '~> 2.3.5'
 # Markdown and HTML processing
 gem 'html-pipeline', '~> 1.11.0'
 gem 'task_list',     '~> 1.0.2', require: 'task_list/railtie'
-gem 'github-markup', '~> 1.3.1'
+gem 'github-markup', '~> 1.4'
 gem 'redcarpet',     '~> 3.3.3'
 gem 'RedCloth',      '~> 4.3.2'
 gem 'rdoc',          '~>3.6'
@@ -113,7 +113,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.11'
+gem 'rouge',         '~> 2.0'
 
 # See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
 # and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
@@ -299,7 +299,7 @@ group :development, :test do
   gem 'spring-commands-spinach',  '~> 1.1.0'
   gem 'spring-commands-teaspoon', '~> 0.0.2'
 
-  gem 'rubocop', '~> 0.40.0', require: false
+  gem 'rubocop', '~> 0.41.2', require: false
   gem 'rubocop-rspec', '~> 1.5.0', require: false
   gem 'scss_lint', '~> 0.47.0', require: false
   gem 'simplecov', '~> 0.11.0', require: false
@@ -344,7 +344,7 @@ gem 'oauth2', '~> 1.2.0'
 gem 'paranoia', '~> 2.0'
 
 # Health check
-gem 'health_check', '~> 1.5.1'
+gem 'health_check', '~> 2.1.0'
 
 # System information
 gem 'vmstat', '~> 2.1.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 721ab9ddc5d2b7cacfd491e31fd0a68b48e3aad5..0987fd5665a02dc5b91aefb2e16a99f200341dfd 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -3,34 +3,34 @@ GEM
   specs:
     RedCloth (4.3.2)
     ace-rails-ap (4.0.2)
-    actionmailer (4.2.6)
-      actionpack (= 4.2.6)
-      actionview (= 4.2.6)
-      activejob (= 4.2.6)
+    actionmailer (4.2.7)
+      actionpack (= 4.2.7)
+      actionview (= 4.2.7)
+      activejob (= 4.2.7)
       mail (~> 2.5, >= 2.5.4)
       rails-dom-testing (~> 1.0, >= 1.0.5)
-    actionpack (4.2.6)
-      actionview (= 4.2.6)
-      activesupport (= 4.2.6)
+    actionpack (4.2.7)
+      actionview (= 4.2.7)
+      activesupport (= 4.2.7)
       rack (~> 1.6)
       rack-test (~> 0.6.2)
       rails-dom-testing (~> 1.0, >= 1.0.5)
       rails-html-sanitizer (~> 1.0, >= 1.0.2)
-    actionview (4.2.6)
-      activesupport (= 4.2.6)
+    actionview (4.2.7)
+      activesupport (= 4.2.7)
       builder (~> 3.1)
       erubis (~> 2.7.0)
       rails-dom-testing (~> 1.0, >= 1.0.5)
       rails-html-sanitizer (~> 1.0, >= 1.0.2)
-    activejob (4.2.6)
-      activesupport (= 4.2.6)
+    activejob (4.2.7)
+      activesupport (= 4.2.7)
       globalid (>= 0.3.0)
-    activemodel (4.2.6)
-      activesupport (= 4.2.6)
+    activemodel (4.2.7)
+      activesupport (= 4.2.7)
       builder (~> 3.1)
-    activerecord (4.2.6)
-      activemodel (= 4.2.6)
-      activesupport (= 4.2.6)
+    activerecord (4.2.7)
+      activemodel (= 4.2.7)
+      activesupport (= 4.2.7)
       arel (~> 6.0)
     activerecord-session_store (1.0.0)
       actionpack (>= 4.0, < 5.1)
@@ -38,7 +38,7 @@ GEM
       multi_json (~> 1.11, >= 1.11.2)
       rack (>= 1.5.2, < 3)
       railties (>= 4.0, < 5.1)
-    activesupport (4.2.6)
+    activesupport (4.2.7)
       i18n (~> 0.7)
       json (~> 1.7, >= 1.7.7)
       minitest (~> 5.1)
@@ -58,7 +58,7 @@ GEM
       faraday_middleware-multi_json (~> 0.0)
       oauth2 (~> 1.0)
     asciidoctor (1.5.3)
-    ast (2.2.0)
+    ast (2.3.0)
     attr_encrypted (3.0.1)
       encryptor (~> 3.0.0)
     attr_required (1.0.0)
@@ -264,7 +264,7 @@ GEM
       escape_utils (~> 1.1.0)
       mime-types (>= 1.19)
       rugged (>= 0.23.0b)
-    github-markup (1.3.3)
+    github-markup (1.4.0)
     gitlab-flowdock-git-hook (1.0.1)
       flowdock (~> 0.7)
       gitlab-grit (>= 2.4.1)
@@ -287,13 +287,13 @@ GEM
       rubyntlm (~> 0.3)
     globalid (0.3.6)
       activesupport (>= 4.1.0)
-    gollum-grit_adapter (1.0.0)
+    gollum-grit_adapter (1.0.1)
       gitlab-grit (~> 2.7, >= 2.7.1)
-    gollum-lib (4.1.0)
-      github-markup (~> 1.3.3)
+    gollum-lib (4.2.1)
+      github-markup (~> 1.4.0)
       gollum-grit_adapter (~> 1.0)
       nokogiri (~> 1.6.4)
-      rouge (~> 1.9)
+      rouge (~> 2.0)
       sanitize (~> 2.1.0)
       stringex (~> 2.5.1)
     gollum-rugged_adapter (0.4.2)
@@ -322,8 +322,8 @@ GEM
       thor
       tilt
     hashie (3.4.3)
-    health_check (1.5.1)
-      rails (>= 2.3.0)
+    health_check (2.1.0)
+      rails (>= 4.0)
     hipchat (1.5.2)
       httparty
       mimemagic
@@ -473,7 +473,7 @@ GEM
     orm_adapter (0.5.0)
     paranoia (2.1.4)
       activerecord (~> 4.0)
-    parser (2.3.1.0)
+    parser (2.3.1.2)
       ast (~> 2.2)
     pg (0.18.4)
     pkg-config (1.1.7)
@@ -515,16 +515,16 @@ GEM
       rack
     rack-test (0.6.3)
       rack (>= 1.0)
-    rails (4.2.6)
-      actionmailer (= 4.2.6)
-      actionpack (= 4.2.6)
-      actionview (= 4.2.6)
-      activejob (= 4.2.6)
-      activemodel (= 4.2.6)
-      activerecord (= 4.2.6)
-      activesupport (= 4.2.6)
+    rails (4.2.7)
+      actionmailer (= 4.2.7)
+      actionpack (= 4.2.7)
+      actionview (= 4.2.7)
+      activejob (= 4.2.7)
+      activemodel (= 4.2.7)
+      activerecord (= 4.2.7)
+      activesupport (= 4.2.7)
       bundler (>= 1.3.0, < 2.0)
-      railties (= 4.2.6)
+      railties (= 4.2.7)
       sprockets-rails
     rails-deprecated_sanitizer (1.0.3)
       activesupport (>= 4.2.0.alpha)
@@ -534,9 +534,9 @@ GEM
       rails-deprecated_sanitizer (>= 1.0.1)
     rails-html-sanitizer (1.0.3)
       loofah (~> 2.0)
-    railties (4.2.6)
-      actionpack (= 4.2.6)
-      activesupport (= 4.2.6)
+    railties (4.2.7)
+      actionpack (= 4.2.7)
+      activesupport (= 4.2.7)
       rake (>= 0.8.7)
       thor (>= 0.18.1, < 2.0)
     rainbow (2.1.0)
@@ -578,7 +578,7 @@ GEM
       railties (>= 4.2.0, < 5.1)
     rinku (2.0.0)
     rotp (2.1.2)
-    rouge (1.11.0)
+    rouge (2.0.3)
     rqrcode (0.7.0)
       chunky_png
     rqrcode-rails3 (0.1.7)
@@ -606,8 +606,8 @@ GEM
     rspec-retry (0.4.5)
       rspec-core
     rspec-support (3.5.0)
-    rubocop (0.40.0)
-      parser (>= 2.3.1.0, < 3.0)
+    rubocop (0.41.2)
+      parser (>= 2.3.1.1, < 3.0)
       powerpack (~> 0.1)
       rainbow (>= 1.99.1, < 3.0)
       ruby-progressbar (~> 1.7)
@@ -697,7 +697,7 @@ GEM
       spring (>= 0.9.1)
     spring-commands-teaspoon (0.0.2)
       spring (>= 0.9.1)
-    sprockets (3.6.2)
+    sprockets (3.6.3)
       concurrent-ruby (~> 1.0)
       rack (> 1, < 3)
     sprockets-rails (3.1.1)
@@ -758,7 +758,7 @@ GEM
     unf (0.1.4)
       unf_ext
     unf_ext (0.0.7.2)
-    unicode-display_width (1.0.5)
+    unicode-display_width (1.1.0)
     unicorn (4.9.0)
       kgio (~> 2.6)
       rack
@@ -859,18 +859,18 @@ DEPENDENCIES
   gemnasium-gitlab-service (~> 0.2)
   gemojione (~> 2.6)
   github-linguist (~> 4.7.0)
-  github-markup (~> 1.3.1)
+  github-markup (~> 1.4)
   gitlab-flowdock-git-hook (~> 1.0.1)
   gitlab_git (~> 10.2)
   gitlab_meta (= 7.0)
   gitlab_omniauth-ldap (~> 1.2.1)
-  gollum-lib (~> 4.1.0)
+  gollum-lib (~> 4.2)
   gollum-rugged_adapter (~> 0.4.2)
   gon (~> 6.0.1)
   grape (~> 0.13.0)
   grape-entity (~> 0.4.2)
   hamlit (~> 2.5)
-  health_check (~> 1.5.1)
+  health_check (~> 2.1.0)
   hipchat (~> 1.5.0)
   html-pipeline (~> 1.11.0)
   httparty (~> 0.13.3)
@@ -920,7 +920,7 @@ DEPENDENCIES
   rack-attack (~> 4.3.1)
   rack-cors (~> 0.4.0)
   rack-oauth2 (~> 1.2.1)
-  rails (= 4.2.6)
+  rails (= 4.2.7)
   rails-deprecated_sanitizer (~> 1.0.3)
   rainbow (~> 2.1.0)
   rblineprof (~> 0.3.6)
@@ -933,11 +933,11 @@ DEPENDENCIES
   request_store (~> 1.3.0)
   rerun (~> 0.11.0)
   responders (~> 2.0)
-  rouge (~> 1.11)
+  rouge (~> 2.0)
   rqrcode-rails3 (~> 0.1.7)
   rspec-rails (~> 3.5.0)
   rspec-retry (~> 0.4.5)
-  rubocop (~> 0.40.0)
+  rubocop (~> 0.41.2)
   rubocop-rspec (~> 1.5.0)
   ruby-fogbugz (~> 0.2.1)
   sanitize (~> 2.0)
diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee
index cf46f15a15602455aa13154d6b994db8718c9dfc..89b0ac697ed52d232b27203f3da4575c74fc03be 100644
--- a/app/assets/javascripts/api.js.coffee
+++ b/app/assets/javascripts/api.js.coffee
@@ -3,7 +3,7 @@
   groupPath: "/api/:version/groups/:id.json"
   namespacesPath: "/api/:version/namespaces.json"
   groupProjectsPath: "/api/:version/groups/:id/projects.json"
-  projectsPath: "/api/:version/projects.json"
+  projectsPath: "/api/:version/projects.json?simple=true"
   labelsPath: "/api/:version/projects/:id/labels"
   licensePath: "/api/:version/licenses/:key"
   gitignorePath: "/api/:version/gitignores/:key"
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index 64da503c35f1d504abe7e40a579708d56e828311..eceff6d91d5e8bcc4c50d400dcf781763fc11497 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -47,14 +47,12 @@
 #= require date.format
 #= require_directory ./behaviors
 #= require_directory ./blob
-#= require_directory ./ci
 #= require_directory ./commit
 #= require_directory ./extensions
 #= require_directory ./lib/utils
 #= require_directory ./u2f
 #= require_directory .
 #= require fuzzaldrin-plus
-#= require u2f
 
 window.slugify = (text) ->
   text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
diff --git a/app/assets/javascripts/ci/build.coffee b/app/assets/javascripts/build.coffee
similarity index 97%
rename from app/assets/javascripts/ci/build.coffee
rename to app/assets/javascripts/build.coffee
index 74691b2c1b516b260dc01ce87428c8fadc177e18..cf203ea43a0c7c9ecbf3c76ba4194d9553ca4499 100644
--- a/app/assets/javascripts/ci/build.coffee
+++ b/app/assets/javascripts/build.coffee
@@ -1,9 +1,9 @@
-class @CiBuild
+class @Build
   @interval: null
   @state: null
 
   constructor: (@page_url, @build_url, @build_status, @state) ->
-    clearInterval(CiBuild.interval)
+    clearInterval(Build.interval)
 
     # Init breakpoint checker
     @bp = Breakpoints.get()
@@ -40,7 +40,7 @@ class @CiBuild
       # Check for new build output if user still watching build page
       # Only valid for runnig build when output changes during time
       #
-      CiBuild.interval = setInterval =>
+      Build.interval = setInterval =>
         if window.location.href.split("#").first() is @page_url
           @getBuildTrace()
       , 4000
diff --git a/app/assets/javascripts/ci/application.js.coffee b/app/assets/javascripts/ci/application.js.coffee
deleted file mode 100644
index ca24c1d759f8798595649fc3176ffdd3974e0d6c..0000000000000000000000000000000000000000
--- a/app/assets/javascripts/ci/application.js.coffee
+++ /dev/null
@@ -1,12 +0,0 @@
-#= require pager
-#= require jquery_nested_form
-#= require_tree .
-
-$(document).on 'click', '.assign-all-runner', ->
-  $(this).replaceWith('<i class="fa fa-refresh fa-spin"></i> Assign in progress..')
-
-window.unbindEvents = ->
-  $(document).unbind('scroll')
-  $(document).off('scroll')
-
-document.addEventListener("page:fetch", unbindEvents)
diff --git a/app/assets/javascripts/ci/projects.js.coffee b/app/assets/javascripts/ci/projects.js.coffee
deleted file mode 100644
index e6406011d11438a454784b786f62a7c22d38a30f..0000000000000000000000000000000000000000
--- a/app/assets/javascripts/ci/projects.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-$(document).on 'click', '.badge-codes-toggle', ->
-  $('.badge-codes-block').toggleClass("hide")
-  return false
diff --git a/app/assets/javascripts/compare_autocomplete.js.coffee b/app/assets/javascripts/compare_autocomplete.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..7ad9fd9763702af7cef72080d1d0a89f9a51e9bb
--- /dev/null
+++ b/app/assets/javascripts/compare_autocomplete.js.coffee
@@ -0,0 +1,41 @@
+class @CompareAutocomplete
+  constructor: ->
+    @initDropdown()
+
+  initDropdown: ->
+    $('.js-compare-dropdown').each ->
+      $dropdown = $(@)
+      selected = $dropdown.data('selected')
+
+      $dropdown.glDropdown(
+        data: (term, callback) ->
+          $.ajax(
+            url: $dropdown.data('refs-url')
+            data:
+              ref: $dropdown.data('ref')
+          ).done (refs) ->
+            callback(refs)
+        selectable: true
+        filterable: true
+        filterByText: true
+        fieldName: $dropdown.attr('name')
+        filterInput: 'input[type="text"]'
+        renderRow: (ref) ->
+          if ref.header?
+            $('<li />')
+              .addClass('dropdown-header')
+              .text(ref.header)
+          else
+            link = $('<a />')
+              .attr('href', '#')
+              .addClass(if ref is selected then 'is-active' else '')
+              .text(ref)
+              .attr('data-ref', escape(ref))
+
+            $('<li />')
+              .append(link)
+        id: (obj, $el) ->
+          $el.attr('data-ref')
+        toggleLabel: (obj, $el) ->
+          $el.text().trim()
+      )
diff --git a/app/assets/javascripts/diff.js.coffee b/app/assets/javascripts/diff.js.coffee
index 6d9b364cb8d2e4f7d452202ec4d488c00769feb1..c132cc8c542d2c54783c3808b4b054362065718e 100644
--- a/app/assets/javascripts/diff.js.coffee
+++ b/app/assets/javascripts/diff.js.coffee
@@ -1,6 +1,9 @@
 class @Diff
   UNFOLD_COUNT = 20
   constructor: ->
+    $('.files .diff-file').singleFileDiff()
+    @filesCommentButton = $('.files .diff-file').filesCommentButton()
+
     $(document).off('click', '.js-unfold')
     $(document).on('click', '.js-unfold', (event) =>
       target = $(event.target)
@@ -36,7 +39,7 @@ class @Diff
         # see https://gitlab.com/gitlab-org/gitlab-ce/issues/707
         indent: 1
 
-      $.get(link, params, (response) =>
+      $.get(link, params, (response) ->
         target.parent().replaceWith(response)
       )
     )
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index 74fd77cf7abc987912bb8d991aaf9d69cfc781e7..afaa6407b05df0222487fa6145c11b899aec9e22 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -137,6 +137,8 @@ class Dispatcher
         new Project()
         new ProjectAvatar()
         switch path[1]
+          when 'compare'
+            new CompareAutocomplete()
           when 'edit'
             shortcut_handler = new ShortcutsNavigation()
             new ProjectNew()
diff --git a/app/assets/javascripts/files_comment_button.js.coffee b/app/assets/javascripts/files_comment_button.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..db0bf7082a99441716248d10704a446c828306da
--- /dev/null
+++ b/app/assets/javascripts/files_comment_button.js.coffee
@@ -0,0 +1,97 @@
+class @FilesCommentButton
+  COMMENT_BUTTON_CLASS = '.add-diff-note'
+  COMMENT_BUTTON_TEMPLATE = _.template '<button name="button" type="submit" class="btn <%- COMMENT_BUTTON_CLASS %> js-add-diff-note-button" title="Add a comment to this line"><i class="fa fa-comment-o"></i></button>'
+  LINE_HOLDER_CLASS = '.line_holder'
+  LINE_NUMBER_CLASS = 'diff-line-num'
+  LINE_CONTENT_CLASS = 'line_content'
+  UNFOLDABLE_LINE_CLASS = 'js-unfold'
+  EMPTY_CELL_CLASS = 'empty-cell'
+  OLD_LINE_CLASS = 'old_line'
+  NEW_CLASS = 'new'
+  LINE_COLUMN_CLASSES = ".#{LINE_NUMBER_CLASS}, .line_content"
+  TEXT_FILE_SELECTOR = '.text-file'
+  DEBOUNCE_TIMEOUT_DURATION = 100
+
+  constructor: (@filesContainerElement) ->
+    @VIEW_TYPE = $('input#view[type=hidden]').val()
+
+    debounce = _.debounce @render, DEBOUNCE_TIMEOUT_DURATION
+
+    $(document)
+      .on 'mouseover', LINE_COLUMN_CLASSES, debounce
+      .on 'mouseleave', LINE_COLUMN_CLASSES, @destroy
+
+  render: (e) =>
+    $currentTarget = $(e.currentTarget)
+    buttonParentElement = @getButtonParent $currentTarget
+    return unless @shouldRender e, buttonParentElement
+
+    textFileElement = @getTextFileElement $currentTarget
+    lineContentElement = @getLineContent $currentTarget
+
+    buttonParentElement.append @buildButton
+      noteableType: textFileElement.attr 'data-noteable-type'
+      noteableID: textFileElement.attr 'data-noteable-id'
+      commitID: textFileElement.attr 'data-commit-id'
+      noteType: lineContentElement.attr 'data-note-type'
+      position: lineContentElement.attr 'data-position'
+      lineType: lineContentElement.attr 'data-line-type'
+      discussionID: lineContentElement.attr 'data-discussion-id'
+      lineCode: lineContentElement.attr 'data-line-code'
+    return
+
+  destroy: (e) =>
+    return if @isMovingToSameType e
+    $(COMMENT_BUTTON_CLASS, @getButtonParent $(e.currentTarget)).remove()
+    return
+
+  buildButton: (buttonAttributes) ->
+    initializedButtonTemplate = COMMENT_BUTTON_TEMPLATE
+      COMMENT_BUTTON_CLASS: COMMENT_BUTTON_CLASS.substr 1
+    $(initializedButtonTemplate).attr
+      'data-noteable-type': buttonAttributes.noteableType
+      'data-noteable-id': buttonAttributes.noteableID
+      'data-commit-id': buttonAttributes.commitID
+      'data-note-type': buttonAttributes.noteType
+      'data-line-code': buttonAttributes.lineCode
+      'data-position': buttonAttributes.position
+      'data-discussion-id': buttonAttributes.discussionID
+      'data-line-type': buttonAttributes.lineType
+
+  getTextFileElement: (hoveredElement) ->
+    $(hoveredElement.closest TEXT_FILE_SELECTOR)
+
+  getLineContent: (hoveredElement) ->
+    return hoveredElement if hoveredElement.hasClass LINE_CONTENT_CLASS
+
+    $(".#{LINE_CONTENT_CLASS + @diffTypeClass hoveredElement}", hoveredElement.parent())
+
+  getButtonParent: (hoveredElement) ->
+    if @VIEW_TYPE is 'inline'
+      return hoveredElement if hoveredElement.hasClass OLD_LINE_CLASS
+
+      $(".#{OLD_LINE_CLASS}", hoveredElement.parent())
+    else
+      return hoveredElement if hoveredElement.hasClass LINE_NUMBER_CLASS
+
+      $(".#{LINE_NUMBER_CLASS + @diffTypeClass hoveredElement}", hoveredElement.parent())
+
+  diffTypeClass: (hoveredElement) ->
+    if hoveredElement.hasClass(NEW_CLASS) then '.new' else '.old'
+
+  isMovingToSameType: (e) ->
+    newButtonParent = @getButtonParent $(e.toElement)
+    return false unless newButtonParent
+    newButtonParent.is @getButtonParent $(e.currentTarget)
+
+  shouldRender: (e, buttonParentElement) ->
+    (not buttonParentElement.hasClass(EMPTY_CELL_CLASS) and \
+    not buttonParentElement.hasClass(UNFOLDABLE_LINE_CLASS) and \
+    $(COMMENT_BUTTON_CLASS, buttonParentElement).length is 0)
+
+$.fn.filesCommentButton = ->
+  return unless this and @parent().data('can-create-note')?
+
+  @each ->
+    unless $.data this, 'filesCommentButton'
+      $.data this, 'filesCommentButton', new FilesCommentButton $(this)
diff --git a/app/assets/javascripts/flash.js.coffee b/app/assets/javascripts/flash.js.coffee
index b76d214790af2bd459d95d45b5e8b73de5729cb5..5a493041538c82750b9981388bc5c42bdc1fe19b 100644
--- a/app/assets/javascripts/flash.js.coffee
+++ b/app/assets/javascripts/flash.js.coffee
@@ -1,24 +1,28 @@
 class @Flash
-  constructor: (message, type = 'alert')->
-    @flash = $(".flash-container")
-    @flash.html("")
+  hideFlash = -> $(@).fadeOut()
 
-    innerDiv = $('<div/>',
+  constructor: (message, type = 'alert', parent = null)->
+    if parent
+      @flashContainer = parent.find('.flash-container')
+    else
+      @flashContainer = $('.flash-container-page')
+
+    @flashContainer.html('')
+
+    flash = $('<div/>',
       class: "flash-#{type}"
     )
-    innerDiv.appendTo(".flash-container")
+    flash.on 'click', hideFlash
 
-    textDiv = $("<div/>",
-      class: "flash-text",
+    textDiv = $('<div/>',
+      class: 'flash-text',
       text: message
     )
-    textDiv.appendTo(innerDiv)
+    textDiv.appendTo(flash)
 
-    if @flash.parent().hasClass('content-wrapper')
+    if @flashContainer.parent().hasClass('content-wrapper')
       textDiv.addClass('container-fluid container-limited')
 
-    @flash.click -> $(@).fadeOut()
-    @flash.show()
+    flash.appendTo(@flashContainer)
+    @flashContainer.show()
 
-  pinTo: (selector) ->
-    @flash.detach().appendTo(selector)
diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee
index 1c65e833d47bce60e2bdcd27c006b1b441f96e8c..1b0d0db89542b8361aaeb3a0905b223b35f7254f 100644
--- a/app/assets/javascripts/gl_dropdown.js.coffee
+++ b/app/assets/javascripts/gl_dropdown.js.coffee
@@ -456,6 +456,8 @@ class GitLabDropdown
 
   rowClicked: (el) ->
     fieldName = @options.fieldName
+    isInput = $(@el).is('input')
+
     if @renderedData
       groupName = el.data('group')
       if groupName
@@ -466,10 +468,19 @@ class GitLabDropdown
         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 isInput
+      field = $(@el)
+    else
+      field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']")
+
     if el.hasClass(ACTIVE_CLASS)
       el.removeClass(ACTIVE_CLASS)
-      field.remove()
+
+      if isInput
+        field.val('')
+      else
+        field.remove()
 
       # Toggle the dropdown label
       if @options.toggleLabel
@@ -490,7 +501,9 @@ class GitLabDropdown
     else
       if not @options.multiSelect or el.hasClass('dropdown-clear-active')
         @dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS
-        @dropdown.parent().find("input[name='#{fieldName}']").remove()
+
+        unless isInput
+          @dropdown.parent().find("input[name='#{fieldName}']").remove()
 
       if !value?
         field.remove()
@@ -505,7 +518,9 @@ class GitLabDropdown
         if !field.length and fieldName
           @addInput(fieldName, value)
         else
-          field.val value
+          field
+            .val value
+            .trigger 'change'
 
       return selectedObject
 
diff --git a/app/assets/javascripts/issuable.js.coffee b/app/assets/javascripts/issuable.js.coffee
index c71d4ecf5050adb2c5b51d37897b53e0beec3da1..7f795f8096b7080759f880123581e0189a609244 100644
--- a/app/assets/javascripts/issuable.js.coffee
+++ b/app/assets/javascripts/issuable.js.coffee
@@ -32,13 +32,11 @@ issuable_created = false
           $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
+          Issuable.filterResults $form if $search.val() isnt ''
         , 500)
 
   initLabelFilterRemove: ->
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js.coffee b/app/assets/javascripts/lib/utils/datetime_utility.js.coffee
index 948d6dbf07ee4211f1338f836073d37fe934772c..178963fe0aa2bfa5810677bb49d80da41973c9d6 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js.coffee
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js.coffee
@@ -2,10 +2,14 @@
 
   w.gl ?= {}
   w.gl.utils ?= {}
+  w.gl.utils.days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
 
   w.gl.utils.formatDate = (datetime) ->
     dateFormat(datetime, 'mmm d, yyyy h:MMtt Z')
 
+  w.gl.utils.getDayName = (date) ->
+    this.days[date.getDay()]
+
   w.gl.utils.localTimeAgo = ($timeagoEls, setTimeago = true) ->
     $timeagoEls.each( ->
           $el = $(@)
diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee
index 894f80586f183803f8afece075038b018085cdd0..86539e0d725a81605b1143a1fba9f8acf04c3b63 100644
--- a/app/assets/javascripts/merge_request_tabs.js.coffee
+++ b/app/assets/javascripts/merge_request_tabs.js.coffee
@@ -153,17 +153,18 @@ class @MergeRequestTabs
 
   loadDiff: (source) ->
     return if @diffsLoaded
-
     @_get
       url: "#{source}.json" + @_location.search
       success: (data) =>
         $('#diffs').html data.html
         gl.utils.localTimeAgo($('.js-timeago', 'div#diffs'))
         $('#diffs .js-syntax-highlight').syntaxHighlight()
+        $('#diffs .diff-file').singleFileDiff()
         @expandViewContainer() if @diffViewType() is 'parallel'
         @diffsLoaded = true
         @scrollToElement("#diffs")
         @highlighSelectedLine()
+        @filesCommentButton = $('.files .diff-file').filesCommentButton()
 
         $(document)
           .off 'click', '.diff-line-num a'
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index 0b7d8f64456f97131d18d02e9054c73a16908193..0ea54faae1a078b3c22c38fc757e3cc7f0196f4e 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -194,8 +194,7 @@ class @Notes
   renderNote: (note) ->
     unless note.valid
       if note.award
-        flash = new Flash('You have already awarded this emoji!', 'alert')
-        flash.pinTo('.header-content')
+        new Flash('You have already awarded this emoji!', 'alert')
       return
 
     if note.award
@@ -325,6 +324,8 @@ class @Notes
     form.find("#note_position").remove()
     form.find("#note_type").remove()
 
+    @parentTimeline = form.parents('.timeline')
+
   ###
   General note form setup.
 
@@ -357,8 +358,7 @@ class @Notes
     @renderNote(note)
 
   addNoteError: (xhr, note, status) =>
-    flash = new Flash('Your comment could not be submitted! Please check your network connection and try again.', 'alert')
-    flash.pinTo('.md-area')
+    new Flash('Your comment could not be submitted! Please check your network connection and try again.', 'alert', @parentTimeline)
 
   ###
   Called in response to the new note form being submitted
diff --git a/app/assets/javascripts/projects_list.js.coffee b/app/assets/javascripts/projects_list.js.coffee
index e4c4bf3b273043b2492b317c856c8c1e9c219c07..a7d78d9e4612f1dceac097ca1efde6057f7e65c1 100644
--- a/app/assets/javascripts/projects_list.js.coffee
+++ b/app/assets/javascripts/projects_list.js.coffee
@@ -5,13 +5,12 @@
     this.initPagination()
 
   initSearch: ->
-    @timer = null
-    $(".projects-list-filter").on('keyup', ->
-      clearTimeout(@timer)
-      @timer = setTimeout(ProjectsList.filterResults, 500)
-    )
+    projectsListFilter = $('.projects-list-filter')
+    debounceFilter = _.debounce ProjectsList.filterResults, 500
+    projectsListFilter.on 'keyup', (e) ->
+      debounceFilter() if projectsListFilter.val() isnt ''
 
-  filterResults: =>
+  filterResults: ->
     $('.projects-list-holder').fadeTo(250, 0.5)
 
     form = null
diff --git a/app/assets/javascripts/shortcuts.js.coffee b/app/assets/javascripts/shortcuts.js.coffee
index 3319a67a79d9037d2a5cc6c0f33642f4f0413094..8c8689baceed9f5672eb4480b5aeb0b17c32cf55 100644
--- a/app/assets/javascripts/shortcuts.js.coffee
+++ b/app/assets/javascripts/shortcuts.js.coffee
@@ -2,9 +2,10 @@ class @Shortcuts
   constructor: (skipResetBindings) ->
     @enabledHelp = []
     Mousetrap.reset() if not skipResetBindings
-    Mousetrap.bind('?', @onToggleHelp)
-    Mousetrap.bind('s', Shortcuts.focusSearch)
-    Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview)
+    Mousetrap.bind '?', @onToggleHelp
+    Mousetrap.bind 's', Shortcuts.focusSearch
+    Mousetrap.bind 'f', (e) => @focusFilter e
+    Mousetrap.bind ['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview
     Mousetrap.bind('t', -> Turbolinks.visit(findFileURL)) if findFileURL?
 
   onToggleHelp: (e) =>
@@ -32,10 +33,16 @@ class @Shortcuts
           $('.js-more-help-button').remove()
     )
 
+  focusFilter: (e) ->
+    @filterInput ?= $('input[type=search]', '.nav-controls')
+    @filterInput.focus()
+    e.preventDefault()
+
   @focusSearch: (e) ->
     $('#search').focus()
     e.preventDefault()
 
+
 $(document).on 'click.more_help', '.js-more-help-button', (e) ->
   $(@).remove()
   $('.hidden-shortcut').show()
diff --git a/app/assets/javascripts/single_file_diff.js.coffee b/app/assets/javascripts/single_file_diff.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..f3e225c37287cc1773896284115403c6bdc1d7b8
--- /dev/null
+++ b/app/assets/javascripts/single_file_diff.js.coffee
@@ -0,0 +1,54 @@
+class @SingleFileDiff
+
+  WRAPPER = '<div class="diff-content diff-wrap-lines"></div>'
+  LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>'
+  ERROR_HTML = '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>'
+  COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. Click to expand it.</div>'
+
+  constructor: (@file) ->
+    @content = $('.diff-content', @file)
+    @diffForPath = @content.find('[data-diff-for-path]').data 'diff-for-path'
+    @isOpen = !@diffForPath
+
+    if @diffForPath
+      @collapsedContent = @content
+      @loadingContent = $(WRAPPER).addClass('loading').html(LOADING_HTML).hide()
+      @content = null
+      @collapsedContent.after(@loadingContent)
+    else
+      @collapsedContent = $(WRAPPER).html(COLLAPSED_HTML).hide()
+      @content.after(@collapsedContent)
+
+    @collapsedContent.on 'click', @toggleDiff
+
+    $('.file-title > a', @file).on 'click', @toggleDiff
+
+  toggleDiff: (e) =>
+    @isOpen = !@isOpen
+    if not @isOpen and not @hasError
+      @content.hide()
+      @collapsedContent.show()
+    else if @content
+      @collapsedContent.hide()
+      @content.show()
+    else
+      @getContentHTML()
+
+  getContentHTML: ->
+    @collapsedContent.hide()
+    @loadingContent.show()
+    $.get @diffForPath, (data) =>
+      @loadingContent.hide()
+      if data.html
+        @content = $(data.html)
+        @content.syntaxHighlight()
+      else
+        @hasError = true
+        @content = $(ERROR_HTML)
+      @collapsedContent.after(@content)
+    return
+
+$.fn.singleFileDiff = ->
+  return @each ->
+    if not $.data this, 'singleFileDiff'
+      $.data this, 'singleFileDiff', new SingleFileDiff this
diff --git a/app/assets/javascripts/u2f/authenticate.js.coffee b/app/assets/javascripts/u2f/authenticate.js.coffee
index 6deb902c8de6d7c84f3ab17fd9da7e11e8bf08fc..918c0a560fdd902be56e4098d34d951ac37fc3c8 100644
--- a/app/assets/javascripts/u2f/authenticate.js.coffee
+++ b/app/assets/javascripts/u2f/authenticate.js.coffee
@@ -6,8 +6,20 @@
 class @U2FAuthenticate
   constructor: (@container, u2fParams) ->
     @appId = u2fParams.app_id
-    @challenges = u2fParams.challenges
-    @signRequests = u2fParams.sign_requests
+    @challenge = u2fParams.challenge
+
+    # The U2F Javascript API v1.1 requires a single challenge, with
+    # _no challenges per-request_. The U2F Javascript API v1.0 requires a
+    # challenge per-request, which is done by copying the single challenge
+    # into every request.
+    #
+    # In either case, we don't need the per-request challenges that the server
+    # has generated, so we can remove them.
+    #
+    # Note: The server library fixes this behaviour in (unreleased) version 1.0.0.
+    # This can be removed once we upgrade.
+    # https://github.com/castle/ruby-u2f/commit/103f428071a81cd3d5f80c2e77d522d5029946a4
+    @signRequests = u2fParams.sign_requests.map (request) -> _(request).omit('challenge')
 
   start: () =>
     if U2FUtil.isU2FSupported()
@@ -16,7 +28,7 @@ class @U2FAuthenticate
       @renderNotSupported()
 
   authenticate: () =>
-    u2f.sign(@appId, @challenges, @signRequests, (response) =>
+    u2f.sign(@appId, @challenge, @signRequests, (response) =>
       if response.errorCode
         error = new U2FError(response.errorCode)
         @renderError(error);
diff --git a/app/assets/javascripts/u2f/util.js.coffee b/app/assets/javascripts/u2f/util.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..5ef324f609ddc7446c47b1a63d16081959e3a907
--- /dev/null
+++ b/app/assets/javascripts/u2f/util.js.coffee
@@ -0,0 +1,3 @@
+class @U2FUtil
+  @isU2FSupported: ->
+    window.u2f
diff --git a/app/assets/javascripts/u2f/util.js.coffee.erb b/app/assets/javascripts/u2f/util.js.coffee.erb
deleted file mode 100644
index d59341c38b917ef39f699d120cbe96ed5af668ad..0000000000000000000000000000000000000000
--- a/app/assets/javascripts/u2f/util.js.coffee.erb
+++ /dev/null
@@ -1,15 +0,0 @@
-# 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/users/calendar.js.coffee b/app/assets/javascripts/users/calendar.js.coffee
index c081f023b04b07c972235773751aaaadcdde33b9..c49ba5186f2f36edcdfc1bf957d24654f13d4d5c 100644
--- a/app/assets/javascripts/users/calendar.js.coffee
+++ b/app/assets/javascripts/users/calendar.js.coffee
@@ -87,14 +87,15 @@ class @Calendar
       .attr 'width', @daySize
       .attr 'height', @daySize
       .attr 'title', (stamp) =>
+        date = new Date(stamp.date)
         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')
+        dateText = dateFormat(date, 'mmm d, yyyy')
 
-        "#{contribText}<br />#{date}"
+        "#{contribText}<br />#{gl.utils.getDayName(date)} #{dateText}"
       .attr 'class', 'user-contrib-cell js-tooltip'
       .attr 'fill', (stamp) =>
         if stamp.count isnt 0
diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee
index 4e032ab1ff15d3ea57b6c7c27865d073b2f4dea5..344be811e0dfc82d56717cf987efd2d6aee0eacc 100644
--- a/app/assets/javascripts/users_select.js.coffee
+++ b/app/assets/javascripts/users_select.js.coffee
@@ -56,6 +56,11 @@ class @UsersSelect
               username: ''
               avatar: ''
           $value.html(assigneeTemplate(user))
+
+          $collapsedSidebar
+            .attr('title', user.name)
+            .tooltip('fixTitle')
+
           $collapsedSidebar.html(collapsedAssigneeTemplate(user))
 
 
@@ -63,7 +68,6 @@ class @UsersSelect
         '<% if( avatar ) { %>
         <a class="author_link" href="/u/<%- username %>">
           <img width="24" class="avatar avatar-inline s24" alt="" src="<%- avatar %>">
-          <span class="author">Toni Boehm</span>
         </a>
         <% } else { %>
         <i class="fa fa-user"></i>
diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index bb8d71fbae8711721c4183dd30ca4be1053bb532..8b6ddf8ba18d7542db144b3144cf760073a99672 100644
--- a/app/assets/stylesheets/framework/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
@@ -20,6 +20,7 @@
   }
 
   &.s16 { width: 16px; height: 16px; margin-right: 6px; }
+  &.s20 { width: 20px; height: 20px; margin-right: 7px; }
   &.s24 { width: 24px; height: 24px; margin-right: 8px; }
   &.s26 { width: 26px; height: 26px; margin-right: 8px; }
   &.s32 { width: 32px; height: 32px; margin-right: 10px; }
diff --git a/app/assets/stylesheets/framework/blank.scss b/app/assets/stylesheets/framework/blank.scss
index d28cda6d62d307d6718adbce3a6caf6be7133092..540718197e0a818938b52e667102ce9ec0f86817 100644
--- a/app/assets/stylesheets/framework/blank.scss
+++ b/app/assets/stylesheets/framework/blank.scss
@@ -20,9 +20,12 @@
 
 .blank-state-icon {
   padding-bottom: 20px;
+  color: $gray-darkest;
+  font-size: 56px;
 
-  path {
-    fill: $gray-darkest;
+  path,
+  polygon {
+    fill: currentColor;
   }
 }
 
@@ -37,6 +40,10 @@
   margin-top: 0;
   margin-bottom: $gl-padding;
   font-size: 15px;
+
+  > strong {
+    font-weight: 600;
+  }
 }
 
 .blank-state-welcome-title {
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 41e77a4ac682f5994f1daa118892c2274c4ca0bf..ad94e457cfdd523b234f4ccee5468a15c825ad25 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -16,6 +16,15 @@
   font-weight: normal;
   font-size: 16px;
   line-height: 36px;
+
+  &.diff-collapsed {
+    padding: 5px;
+    cursor: pointer;
+
+    &:hover {
+      background-color: $row-hover;
+    }
+  }
 }
 
 .row-content-block {
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index f8aecd0558d3250f76bf52d30395ea8b7b0c10f5..c1e5305644ba5348549593893b0ac0397fe228f9 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -270,21 +270,6 @@ table {
   }
 }
 
-.dashboard-intro-icon {
-  float: left;
-  text-align: center;
-  font-size: 32px;
-  color: #aaa;
-  width: 60px;
-}
-
-.dashboard-intro-text {
-  display: inline-block;
-  margin-left: -60px;
-  padding-left: 60px;
-  width: 100%;
-}
-
 .btn-sign-in {
   text-shadow: none;
 
diff --git a/app/assets/stylesheets/framework/flash.scss b/app/assets/stylesheets/framework/flash.scss
index a951a2b97fe2baaf7d12ad6b42aa9583afebfa58..0c21d0240b36d05dff2926cd9f516339a20ce0ef 100644
--- a/app/assets/stylesheets/framework/flash.scss
+++ b/app/assets/stylesheets/framework/flash.scss
@@ -1,8 +1,8 @@
 .flash-container {
   cursor: pointer;
   margin: 0;
+  margin-bottom: $gl-padding;
   font-size: 14px;
-  width: 100%;
   z-index: 100;
 
   .flash-notice {
@@ -18,9 +18,27 @@
   }
 
   .flash-notice, .flash-alert {
-    .container-fluid.flash-text {
+    border-radius: $border-radius-default;
+
+    .container-fluid.container-limited.flash-text {
       background: transparent;
     }
   }
+
+  &.flash-container-page {
+    margin-bottom: 0;
+
+    .flash-notice, .flash-alert {
+      border-radius: 0;
+    }
+  }
+}
+
+@media (max-width: $screen-md-min) {
+  ul.notes {
+    .flash-container.timeline-content {
+      margin-left: 0;
+    }
+  }
 }
 
diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss
index d1ff63bd09902cc44d2559838511587a7f7d437f..3673b81f183ff0e34ea288bb34eaa4319841e104 100644
--- a/app/assets/stylesheets/framework/gitlab-theme.scss
+++ b/app/assets/stylesheets/framework/gitlab-theme.scss
@@ -11,7 +11,6 @@
     .toggle-nav-collapse,
     .pin-nav-btn {
       color: $color-light;
-      background: $color;
 
       &:hover {
         color: $white-light;
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 188823054fdaaa489113bac1d2a4bc17c16f6cc3..d52e8f0017292d3efc74b986a6790f2757668fa0 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -3,6 +3,12 @@
   padding-bottom: 25px;
   transition: padding $sidebar-transition-duration;
 
+  &.page-sidebar-pinned {
+    .sidebar-wrapper {
+      @include box-shadow(none);
+    }
+  }
+
   .sidebar-wrapper {
     position: fixed;
     top: 0;
@@ -11,6 +17,7 @@
     height: 100%;
     overflow: hidden;
     transition: width $sidebar-transition-duration;
+    @include box-shadow(2px 0 16px 0 $black-transparent);
   }
 }
 
@@ -48,10 +55,6 @@
   overflow-y: auto;
   overflow-x: hidden;
 
-  @media (min-width: $sidebar-breakpoint) {
-    bottom: 50px;
-  }
-
   &.navbar-collapse {
     padding: 0 !important;
   }
@@ -69,7 +72,7 @@
     }
 
     a {
-      padding: 7px 15px 7px 12px;
+      padding: 7px $gl-sidebar-padding;
       font-size: $gl-font-size;
       line-height: 24px;
       display: block;
@@ -101,7 +104,7 @@
   }
 }
 
-.toggle-nav-collapse {
+.sidebar-action-buttons {
   width: $sidebar_width;
   position: absolute;
   top: 0;
@@ -110,12 +113,37 @@
   padding: 5px 0;
   font-size: 18px;
   line-height: 30px;
+
+  .toggle-nav-collapse {
+    left: 0;
+  }
+
+  .pin-nav-btn {
+    right: 0;
+    display: none;
+
+    @media (min-width: $sidebar-breakpoint) {
+      display: block;
+    }
+
+    .fa {
+      transition: transform .15s;
+    }
+
+    &.is-active {
+      .fa {
+        transform: rotate(90deg);
+      }
+    }
+  }
 }
 
 .nav-header-btn {
-  padding: 10px 5px;
+  padding: 10px $gl-sidebar-padding;
   color: inherit;
   transition-duration: .3s;
+  position: absolute;
+  top: 0;
 
   &:hover,
   &:focus {
@@ -124,30 +152,6 @@
   }
 }
 
-.pin-nav-btn {
-  display: none;
-  position: absolute;
-  left: 0;
-  bottom: 0;
-  height: 50px;
-  width: $sidebar_width;
-  line-height: 30px;
-
-  @media (min-width: $sidebar-breakpoint) {
-    display: block;
-  }
-
-  .fa {
-    transition: transform .15s;
-  }
-
-  &.is-active {
-    .fa {
-      transform: rotate(90deg);
-    }
-  }
-}
-
 .page-sidebar-collapsed {
   padding-left: 0;
 
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 4337fab5d8741bff7cee36781165299bf3e7bd6f..f0e7002e4cd6367ef27344df5fea1dc9872f2798 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -17,6 +17,7 @@ $focus-border-color:    #3aabf0;
 $table-border-color:    #f0f0f0;
 $background-color:      #fafafa;
 $dark-background-color: #f7f7f7;
+$table-text-gray:       #8f8f8f;
 
 /*
  * Text
@@ -63,6 +64,7 @@ $gl-btn-padding: 10px;
 $gl-input-padding: 10px;
 $gl-vert-padding: 6px;
 $gl-padding-top: 10px;
+$gl-sidebar-padding: 22px;
 
 /*
  * Misc
diff --git a/app/assets/stylesheets/pages/admin.scss b/app/assets/stylesheets/pages/admin.scss
index 1d34a7f79aeeac7bd8c89c019512d88f3eba56e9..5607239d92df1805797b42672cee1bf203ece5ad 100644
--- a/app/assets/stylesheets/pages/admin.scss
+++ b/app/assets/stylesheets/pages/admin.scss
@@ -88,13 +88,7 @@
 
   .user-name {
     display: inline-block;
-    font-weight: bold;
-  }
-
-  .controls {
-    > .btn, > .dropdown {
-      margin-left: 5px;
-    }
+    font-weight: 600;
   }
 
   .dropdown {
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index e8f1935d239e95a813e57e34343b5ceb77c2012f..99a2cd306cfb85c672b47e402d6c2c9af2ed7dc9 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -83,14 +83,6 @@
   }
 }
 
-table.builds {
-  .build-link {
-    a {
-      color: $gl-dark-link-color;
-    }
-  }
-}
-
 .build-trace {
   background: $ci-output-bg;
   color: $ci-text-color;
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 85bbf70e1883efead710d5b8bdd2f3dcc4d1e8b1..0298577c494cfb1641a9995af5c49181f1630d58 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -61,7 +61,7 @@
     font-size: 0;
   }
 
-  .btn-transparent {
+  .btn-clipboard, .btn-transparent {
     padding-left: 0;
     padding-right: 0;
   }
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index 701b93884544af36289bb7661c1c6bc76bb720ce..2a3acc3eb4c6028fd37d14ff57f702f7467e47c8 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -38,33 +38,6 @@
       margin-right: 15px;
     }
   }
-
-  &.group-admin {
-    display: -webkit-flex;
-    display: -ms-flexbox;
-    display: flex;
-
-    .group-avatar, .group-details, .group-controls {
-      display: -webkit-flex;
-      display: -ms-flexbox;
-      display: flex;
-    }
-
-    .group-details {
-      flex: 1 1 auto;
-      flex-direction: column;
-      min-width: 0;
-    }
-
-    .group-controls {
-      align-items: center;
-
-      a {
-        margin-left: 5px;
-      }
-    }
-  }
-
 }
 
 .ldap-group-links {
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 4e35ca329e465d291e10557af97522cc28a689e0..0e4d8c140aa2d7a6cdc77d58022ca6e88f9af32f 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -63,8 +63,8 @@ form.edit-issue {
 .merge-request,
 .issue {
   &.today {
-    background: #efe;
-    border-color: #cec;
+    background: #f8feef;
+    border-color: #e1e8d5;
   }
 
   &.closed {
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index 47bfd144930da55caa917262f819597d015948e2..3b1e38fc07ddd44a9d6b346759a181aa14089d1f 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -162,9 +162,15 @@
 }
 
 .filtered-labels {
+  font-size: 0;
+  padding: 12px 16px;
+
   .label-row {
+    margin-top: 4px;
+    margin-bottom: 4px;
+
     &:not(:last-child) {
-      margin-right: 5px;
+      margin-right: 8px;
     }
   }
 
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index d9756b66af0fceeb48c270e6e988cc2478d3c9a9..15c6c9f231aa06cecce6b713ac1186e722a8a749 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -73,11 +73,14 @@
       color: #888;
     }
 
-    &.ci-pending,
-    &.ci-running {
+    &.ci-pending {
       color: $gl-warning;
     }
 
+    &.ci-running {
+      color: $blue-normal;
+    }
+
     &.ci-failed,
     &.ci-error {
       color: $gl-danger;
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 6128868b670a43ff1f3d4a50ceb3a2ad1d45d08e..a0334207c683fe4f933c8b3a454321c13070c749 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -1,15 +1,12 @@
 .pipelines {
   .stage {
-    max-width: 100px;
+    max-width: 80px;
+    width: 80px;
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
   }
 
-  .duration, .finished_at {
-    margin: 4px 0;
-  }
-
   .commit-title {
     margin: 0;
   }
@@ -22,3 +19,156 @@
     margin: 4px;
   }
 }
+
+.content-list {
+
+  &.pipelines,
+  &.builds-content-list {
+    width: 100%;
+    overflow: auto;
+  }
+}
+
+.table.builds {
+  min-width: 1100px;
+
+  tr {
+    th {
+      padding: 16px;
+      border: none;
+    }
+  }
+
+  tbody {
+    border-top-width: 1px;
+  }
+
+  .commit-link {
+
+    a:hover {
+      text-decoration: none;
+    }
+  }
+
+  .branch-commit {
+
+    .branch-name {
+      margin-left: 8px;
+      font-weight: bold;
+      max-width: 180px;
+      overflow: hidden;
+      display: inline-block;
+      white-space: nowrap;
+      vertical-align: top;
+      text-overflow: ellipsis;
+    }
+
+    svg {
+      margin: 0 6px;
+      height: 14px;
+      width: auto;
+      vertical-align: middle;
+    }
+
+    .commit-id {
+      color: $gl-link-color;
+      margin-right: 8px;
+    }
+
+    .commit-title {
+      margin-top: 4px;
+      max-width: 320px;
+      overflow: hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+    }
+
+    .avatar {
+      margin-left: 0;
+    }
+
+    .label {
+      margin-right: 4px;
+    }
+
+    .label-container {
+      font-size: 0;
+
+      .label {
+        margin-top: 5px;
+      }
+    }
+  }
+
+  .duration,
+  .finished-at {
+    color: $table-text-gray;
+    margin: 4px 0;
+
+    .fa {
+      font-size: 12px;
+    }
+
+    svg {
+      height: 12px;
+      width: auto;
+      vertical-align: middle;
+    }
+
+    .fa,
+    svg {
+      margin-right: 5px;
+    }
+  }
+
+  .pipeline-actions {
+
+    .btn {
+      margin: 0;
+      color: $table-text-gray;
+    }
+
+    .cancel-retry-btns {
+      vertical-align: middle;
+
+      .btn:not(:first-child) {
+        margin-left: 8px;
+      }
+    }
+
+    .dropdown-toggle,
+    .dropdown-menu {
+      color: $table-text-gray;
+
+      .fa {
+        color: $table-text-gray;
+        margin-right: 6px;
+        font-size: 14px;
+      }
+    }
+
+    .btn-remove {
+      color: $white-light;
+    }
+
+    .btn-group {
+      &.open {
+        .btn-default {
+          background-color: $white-normal;
+          border-color: $border-white-normal;
+        }
+      }
+    }
+  }
+
+  .build-link {
+
+    a {
+      color: $gl-dark-link-color;
+    }
+  }
+
+  .btn-group.open .dropdown-toggle {
+    box-shadow: none;
+  }
+}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index bce4aac33341375177048846870e0ff696c08d57..ea9f7cf054050abea65ec987b99fc9d0bb891818 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -340,23 +340,30 @@ a.deploy-project-label {
 
 .project-import {
   .form-group {
-    margin-bottom: 0;
+    margin-bottom: 5px;
   }
+
   .import-buttons {
     padding-left: 0;
     display: -webkit-flex;
     display: flex;
     -webkit-flex-wrap: wrap;
     flex-wrap: wrap;
+
     .btn {
-      margin-right: 10px;
-      padding: 8px 12px;
+      margin: 0 10px 10px 0;
+      padding: 8px;
     }
-    &> div {
-      margin-bottom: 14px;
+
+    > div {
       padding-left: 0;
+
       &:last-child {
         margin-bottom: 0;
+
+        .btn {
+          margin-right: 0;
+        }
       }
     }
   }
@@ -475,6 +482,10 @@ pre.light-well {
       a:hover {
         text-decoration: none;
       }
+
+      > span {
+        margin-left: 10px;
+      }
     }
   }
 
@@ -632,3 +643,9 @@ pre.light-well {
     width: 300px;
   }
 }
+
+.compare-form-group {
+  .dropdown-menu {
+    width: 300px;
+  }
+}
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index 9e9b18fdbb870a76be071ca9b8d99d6216a99f47..c9d436d72ba87cbb89f654b012413fd0c331b676 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -185,7 +185,7 @@
     padding-right: $gl-padding + 15px;
   }
 
-  .btn-search {
+  .btn-search, .btn-new {
     width: 100%;
     margin-top: 5px;
 
diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss
index 2370d35924e9e51c02edc48965fd95d1c0cedaf7..c6b053150be4cc3c92ee33ba241349816b6bb26a 100644
--- a/app/assets/stylesheets/pages/status.scss
+++ b/app/assets/stylesheets/pages/status.scss
@@ -32,11 +32,15 @@
       border-color: $gl-gray;
     }
 
-    &.ci-pending,
-    &.ci-running {
+    &.ci-pending {
       color: $gl-warning;
       border-color: $gl-warning;
     }
+
+    &.ci-running {
+      color: $blue-normal;
+      border-color: $blue-normal;
+    }
   }
 
   .ci-status-icon-success {
@@ -45,10 +49,12 @@
   .ci-status-icon-failed {
     color: $gl-danger;
   }
-  .ci-status-icon-running,
   .ci-status-icon-pending {
     color: $gl-warning;
   }
+  .ci-status-icon-running {
+    color: $blue-normal;
+  }
   .ci-status-icon-canceled,
   .ci-status-icon-disabled,
   .ci-status-icon-not-found,
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 5b61270daa8ddeb2c18104a01beb131f1c9cdfcd..42a20e9775fc045699d5729727ca6459083d5367 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -23,12 +23,11 @@
       }
 
       &:hover {
-        cursor: pointer;
-
         td {
           background-color: $row-hover;
           border-top: 1px solid $row-hover-border;
           border-bottom: 1px solid $row-hover-border;
+          cursor: pointer;
         }
       }
 
diff --git a/app/controllers/admin/builds_controller.rb b/app/controllers/admin/builds_controller.rb
index 0db91eaaf2e5b9ad4d3fc3560454b27fee5bbc67..88f3c0e2fd4f3cefc472f72b0661139426036991 100644
--- a/app/controllers/admin/builds_controller.rb
+++ b/app/controllers/admin/builds_controller.rb
@@ -5,8 +5,10 @@ class Admin::BuildsController < Admin::ApplicationController
     @builds = @all_builds.order('created_at DESC')
     @builds =
       case @scope
+      when 'pending'
+        @builds.pending.reverse_order
       when 'running'
-        @builds.running_or_pending.reverse_order
+        @builds.running.reverse_order
       when 'finished'
         @builds.finished
       else
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 9cc31620d9f5313d196a9736334bd58e53c3586b..a1004d9bcea658d97d694bf2cde951c971fa6bf0 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -344,10 +344,6 @@ 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
diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb
index 998b8adc4112573e1da0b908955a6351f5f74f1c..ba07cea569c870111ae7b623aa157732c5d87eb3 100644
--- a/app/controllers/concerns/authenticates_with_two_factor.rb
+++ b/app/controllers/concerns/authenticates_with_two_factor.rb
@@ -57,7 +57,7 @@ module AuthenticatesWithTwoFactor
 
   # 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])
+    if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenge])
       # Remove any lingering user data from login
       session.delete(:otp_user_id)
       session.delete(:challenges)
@@ -77,11 +77,9 @@ module AuthenticatesWithTwoFactor
 
     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? })
+      session[:challenge] ||= u2f.challenge
+      gon.push(u2f: { challenge: session[:challenge], app_id: u2f_app_id,
+                      sign_requests: sign_requests })
     end
   end
 end
diff --git a/app/controllers/concerns/diff_for_path.rb b/app/controllers/concerns/diff_for_path.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e09b8789eb239ef8b83f1eef2d59710ede432de3
--- /dev/null
+++ b/app/controllers/concerns/diff_for_path.rb
@@ -0,0 +1,25 @@
+module DiffForPath
+  extend ActiveSupport::Concern
+
+  def render_diff_for_path(diffs, diff_refs, project)
+    diff_file = safe_diff_files(diffs, diff_refs: diff_refs, repository: project.repository).find do |diff|
+      diff.old_path == params[:old_path] && diff.new_path == params[:new_path]
+    end
+
+    return render_404 unless diff_file
+
+    diff_commit = commit_for_diff(diff_file)
+    blob = diff_file.blob(diff_commit)
+    @expand_all_diffs = true
+
+    locals = {
+      diff_file: diff_file,
+      diff_commit: diff_commit,
+      diff_refs: diff_refs,
+      blob: blob,
+      project: project
+    }
+
+    render json: { html: view_to_html_string('projects/diffs/_content', locals) }
+  end
+end
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index 3a2db3e6eeb16475d61c933f2ac0fc994af195a1..19a76a5b5d8d11a04d8ad179271810d56f6a75c6 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -1,6 +1,4 @@
 class Dashboard::TodosController < Dashboard::ApplicationController
-  include TodosHelper
-
   before_action :find_todos, only: [:index, :destroy_all]
 
   def index
@@ -13,7 +11,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
     respond_to do |format|
       format.html { redirect_to dashboard_todos_path, notice: 'Todo was successfully marked as done.' }
       format.js { head :ok }
-      format.json { render json: { count: todos_pending_count, done_count: todos_done_count } }
+      format.json { render json: todos_counts }
     end
   end
 
@@ -23,7 +21,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
     respond_to do |format|
       format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' }
       format.js { head :ok }
-      format.json { render json: { count: todos_pending_count, done_count: todos_done_count } }
+      format.json { render json: todos_counts }
     end
   end
 
@@ -36,4 +34,11 @@ class Dashboard::TodosController < Dashboard::ApplicationController
   def find_todos
     @todos ||= TodosFinder.new(current_user, params).execute
   end
+
+  def todos_counts
+    {
+      count: TodosFinder.new(current_user, state: :pending).execute.count,
+      done_count: TodosFinder.new(current_user, state: :done).execute.count
+    }
+  end
 end
diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb
index 9b5c43b17e250b03d14da3e7a98d5d189d1b1dbb..d3dd98c8a4e44167dbcb471076e2ba12bed6f08a 100644
--- a/app/controllers/help_controller.rb
+++ b/app/controllers/help_controller.rb
@@ -12,13 +12,12 @@ class HelpController < ApplicationController
   end
 
   def show
-    @category = clean_path_info(path_params[:category])
-    @file = path_params[:file]
+    @path = clean_path_info(path_params[:path])
 
     respond_to do |format|
       format.any(:markdown, :md, :html) do
         # Note: We are purposefully NOT using `Rails.root.join`
-        path = File.join(Rails.root, 'doc', @category, "#{@file}.md")
+        path = File.join(Rails.root, 'doc', "#{@path}.md")
 
         if File.exist?(path)
           @markdown = File.read(path)
@@ -33,7 +32,7 @@ class HelpController < ApplicationController
       # Allow access to images in the doc folder
       format.any(:png, :gif, :jpeg) do
         # Note: We are purposefully NOT using `Rails.root.join`
-        path = File.join(Rails.root, 'doc', @category, "#{@file}.#{params[:format]}")
+        path = File.join(Rails.root, 'doc', "#{@path}.#{params[:format]}")
 
         if File.exist?(path)
           send_file(path, disposition: 'inline')
@@ -57,8 +56,7 @@ class HelpController < ApplicationController
   private
 
   def path_params
-    params.require(:category)
-    params.require(:file)
+    params.require(:path)
 
     params
   end
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index f35d631df0cea82e76bf7f824ab52be3c028d79e..f54c79c2e37abdff2d24879e513cb21b9e1cf505 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -107,7 +107,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
     # Only allow properly saved users to login.
     if @user.persisted? && @user.valid?
       log_audit_event(@user, with: oauth['provider'])
-      sign_in_and_redirect(@user)
+      if @user.two_factor_enabled?
+        prompt_for_two_factor(@user)
+      else
+        sign_in_and_redirect(@user)
+      end
     else
       error_message = @user.errors.full_messages.to_sentence
 
diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb
index 6a358fdcc0583abeba2ae418817c49b848d5a038..e37e9e136dbb363cf18a60fc41937249072cdf7f 100644
--- a/app/controllers/profiles/two_factor_auths_controller.rb
+++ b/app/controllers/profiles/two_factor_auths_controller.rb
@@ -100,7 +100,6 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
 
     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? })
+                    sign_requests: sign_requests })
   end
 end
diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb
index bfe0781d73525999eb8a011faf40e0ddb725f163..f33cf238d88544bfae8c57eff2912c52f8d5c6ed 100644
--- a/app/controllers/projects/artifacts_controller.rb
+++ b/app/controllers/projects/artifacts_controller.rb
@@ -23,10 +23,9 @@ class Projects::ArtifactsController < Projects::ApplicationController
     entry = build.artifacts_metadata_entry(params[:path])
 
     if entry.exists?
-      render json: { archive: build.artifacts_file.path,
-                     entry: Base64.encode64(entry.path) }
+      send_artifacts_entry(build, entry)
     else
-      render json: {}, status: 404
+      render_404
     end
   end
 
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index ef3051d7519c8e4d551b04b13cf7367bab34e4cf..d7513d75f014491e537789b28df6fb385ae02090 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -10,8 +10,10 @@ class Projects::BuildsController < Projects::ApplicationController
     @builds = @all_builds.order('created_at DESC')
     @builds =
       case @scope
+      when 'pending'
+        @builds.pending.reverse_order
       when 'running'
-        @builds.running_or_pending.reverse_order
+        @builds.running.reverse_order
       when 'finished'
         @builds.finished
       else
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 37d6521026c6b2d41f20c5aa7eb8b6e18a813fa9..727e84b40a1ff025f7c81add065f907a95882934 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -3,6 +3,7 @@
 # Not to be confused with CommitsController, plural.
 class Projects::CommitController < Projects::ApplicationController
   include CreatesCommit
+  include DiffForPath
   include DiffHelper
 
   # Authorize
@@ -11,29 +12,14 @@ class Projects::CommitController < Projects::ApplicationController
   before_action :authorize_update_build!, only: [:cancel_builds, :retry_builds]
   before_action :authorize_read_commit_status!, only: [:builds]
   before_action :commit
-  before_action :define_show_vars, only: [:show, :builds]
+  before_action :define_commit_vars, only: [:show, :diff_for_path, :builds]
+  before_action :define_status_vars, only: [:show, :builds]
+  before_action :define_note_vars, only: [:show, :diff_for_path]
   before_action :authorize_edit_tree!, only: [:revert, :cherry_pick]
 
   def show
     apply_diff_view_cookie!
 
-    @grouped_diff_notes = commit.notes.grouped_diff_notes
-    @notes = commit.notes.non_diff_notes.fresh
-    
-    Banzai::NoteRenderer.render(
-      @grouped_diff_notes.values.flatten + @notes,
-      @project,
-      current_user,
-    )
-
-    @note = @project.build_commit_note(commit)
-
-    @noteable = @commit
-    @comments_target = {
-      noteable_type: 'Commit',
-      commit_id: @commit.id
-    }
-
     respond_to do |format|
       format.html
       format.diff  { render text: @commit.to_diff }
@@ -41,6 +27,10 @@ class Projects::CommitController < Projects::ApplicationController
     end
   end
 
+  def diff_for_path
+    render_diff_for_path(@diffs, @commit.diff_refs, @project)
+  end
+
   def builds
   end
 
@@ -114,7 +104,7 @@ class Projects::CommitController < Projects::ApplicationController
     @ci_builds ||= Ci::Build.where(pipeline: pipelines)
   end
 
-  def define_show_vars
+  def define_commit_vars
     return git_not_found! unless commit
 
     opts = diff_options
@@ -122,7 +112,28 @@ class Projects::CommitController < Projects::ApplicationController
 
     @diffs = commit.diffs(opts)
     @notes_count = commit.notes.count
+  end
+
+  def define_note_vars
+    @grouped_diff_notes = commit.notes.grouped_diff_notes
+    @notes = commit.notes.non_diff_notes.fresh
+
+    Banzai::NoteRenderer.render(
+      @grouped_diff_notes.values.flatten + @notes,
+      @project,
+      current_user,
+    )
+
+    @note = @project.build_commit_note(commit)
+
+    @noteable = @commit
+    @comments_target = {
+      noteable_type: 'Commit',
+      commit_id: @commit.id
+    }
+  end
 
+  def define_status_vars
     @statuses = CommitStatus.where(pipeline: pipelines)
     @builds = Ci::Build.where(pipeline: pipelines)
   end
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index d240b9fe989776606a450a4cd156180b14d4702a..5f3ee71444dd8f25e9026298ebc67b744fdbadde 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -1,29 +1,51 @@
 require 'addressable/uri'
 
 class Projects::CompareController < Projects::ApplicationController
+  include DiffForPath
   include DiffHelper
 
   # Authorize
   before_action :require_non_empty_project
   before_action :authorize_download_code!
-  before_action :assign_ref_vars, only: [:index, :show]
+  before_action :define_ref_vars, only: [:index, :show, :diff_for_path]
+  before_action :define_diff_vars, only: [:show, :diff_for_path]
   before_action :merge_request, only: [:index, :show]
 
   def index
   end
 
   def show
-    compare = CompareService.new.
-      execute(@project, @head_ref, @project, @start_ref, diff_options)
+  end
+
+  def diff_for_path
+    return render_404 unless @compare
+
+    render_diff_for_path(@diffs, @diff_refs, @project)
+  end
+
+  def create
+    redirect_to namespace_project_compare_path(@project.namespace, @project,
+                                               params[:from], params[:to])
+  end
+
+  private
 
-    if compare
-      @commits = Commit.decorate(compare.commits, @project)
+  def define_ref_vars
+    @start_ref = Addressable::URI.unescape(params[:from])
+    @ref = @head_ref = Addressable::URI.unescape(params[:to])
+  end
+
+  def define_diff_vars
+    @compare = CompareService.new.execute(@project, @head_ref, @project, @start_ref)
+
+    if @compare
+      @commits = Commit.decorate(@compare.commits, @project)
 
       @start_commit = @project.commit(@start_ref)
       @commit = @project.commit(@head_ref)
       @base_commit = @project.merge_base_commit(@start_ref, @head_ref)
 
-      @diffs = compare.diffs(diff_options)
+      @diffs = @compare.diffs(diff_options)
       @diff_refs = Gitlab::Diff::DiffRefs.new(
         base_sha: @base_commit.try(:sha),
         start_sha: @start_commit.try(:sha),
@@ -35,18 +57,6 @@ class Projects::CompareController < Projects::ApplicationController
     end
   end
 
-  def create
-    redirect_to namespace_project_compare_path(@project.namespace, @project,
-                                               params[:from], params[:to])
-  end
-
-  private
-
-  def assign_ref_vars
-    @start_ref = Addressable::URI.unescape(params[:from])
-    @ref = @head_ref = Addressable::URI.unescape(params[:to])
-  end
-
   def merge_request
     @merge_request ||= @project.merge_requests.opened.
       find_by(source_project: @project, source_branch: @head_ref, target_branch: @start_ref)
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 5678a4015b623f9218bf0ae048ab4575842bb7da..df659bb8c3bbfa3cbd1d4e58a8e91b9dd411c9dd 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -1,5 +1,6 @@
 class Projects::MergeRequestsController < Projects::ApplicationController
   include ToggleSubscriptionAction
+  include DiffForPath
   include DiffHelper
   include IssuableActions
   include ToggleAwardEmoji
@@ -12,6 +13,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
   before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds]
   before_action :define_show_vars, only: [:show, :diffs, :commits, :builds]
   before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check]
+  before_action :define_commit_vars, only: [:diffs]
+  before_action :define_diff_comment_vars, only: [:diffs]
   before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds]
 
   # Allow read any merge_request
@@ -53,8 +56,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
 
   def show
     respond_to do |format|
-      format.html
-      
+      format.html { define_discussion_vars }
+
       format.json do
         render json: @merge_request
       end
@@ -78,35 +81,38 @@ class Projects::MergeRequestsController < Projects::ApplicationController
 
     @merge_request_diff = @merge_request.merge_request_diff
 
-    @commit = @merge_request.diff_head_commit
-    @base_commit = @merge_request.diff_base_commit || @merge_request.likely_diff_base_commit
-
-    @comments_target = {
-      noteable_type: 'MergeRequest',
-      noteable_id: @merge_request.id
-    }
-
-    @use_legacy_diff_notes = !@merge_request.support_new_diff_notes?
-    @grouped_diff_notes = @merge_request.notes.grouped_diff_notes
-
-    Banzai::NoteRenderer.render(
-      @grouped_diff_notes.values.flatten,
-      @project,
-      current_user,
-      @path,
-      @project_wiki,
-      @ref
-    )
-
     respond_to do |format|
-      format.html
+      format.html { define_discussion_vars }
       format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } }
     end
   end
 
+  # With an ID param, loads the MR at that ID. Otherwise, accepts the same params as #new
+  # and uses that (unsaved) MR.
+  #
+  def diff_for_path
+    if params[:id]
+      merge_request
+      define_diff_comment_vars
+    else
+      build_merge_request
+      @diff_notes_disabled = true
+      @grouped_diff_notes = {}
+    end
+
+    define_commit_vars
+    diffs = @merge_request.diffs(diff_options)
+
+    render_diff_for_path(diffs, @merge_request.diff_refs, @merge_request.project)
+  end
+
   def commits
     respond_to do |format|
-      format.html { render 'show' }
+      format.html do
+        define_discussion_vars
+
+        render 'show'
+      end
       format.json do
         # Get commits from repository
         # or from cache if already merged
@@ -121,14 +127,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController
 
   def builds
     respond_to do |format|
-      format.html { render 'show' }
+      format.html do
+        define_discussion_vars
+
+        render 'show'
+      end
       format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_builds') } }
     end
   end
 
   def new
-    params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
-    @merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute
+    build_merge_request
     @noteable = @merge_request
 
     @target_branches = if @merge_request.target_project
@@ -352,14 +361,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
       @merge_request.unlock_mr
       @merge_request.close
     end
-
-    if request.format == :html || action_name == 'show'
-      define_show_html_vars
-    end
   end
 
-  # Discussion tab data is only required on html requests
-  def define_show_html_vars
+  # Discussion tab data is rendered on html responses of actions
+  # :show, :diff, :commits, :builds. but not when request the data through AJAX
+  def define_discussion_vars
     # Build a note object for comment form
     @note = @project.notes.new(noteable: @noteable)
 
@@ -384,6 +390,30 @@ class Projects::MergeRequestsController < Projects::ApplicationController
     @pipelines = [@pipeline].compact
   end
 
+  def define_commit_vars
+    @commit = @merge_request.diff_head_commit
+    @base_commit = @merge_request.diff_base_commit || @merge_request.likely_diff_base_commit
+  end
+
+  def define_diff_comment_vars
+    @comments_target = {
+      noteable_type: 'MergeRequest',
+      noteable_id: @merge_request.id
+    }
+
+    @use_legacy_diff_notes = !@merge_request.support_new_diff_notes?
+    @grouped_diff_notes = @merge_request.notes.grouped_diff_notes
+
+    Banzai::NoteRenderer.render(
+      @grouped_diff_notes.values.flatten,
+      @project,
+      current_user,
+      @path,
+      @project_wiki,
+      @ref
+    )
+  end
+
   def invalid_mr
     # Render special view for MR with removed source or target branch
     render 'invalid'
@@ -412,4 +442,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
     params[:merge_when_build_succeeds].present? &&
       @merge_request.pipeline && @merge_request.pipeline.active?
   end
+
+  def build_merge_request
+    params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
+    @merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute
+  end
 end
diff --git a/app/controllers/projects/todos_controller.rb b/app/controllers/projects/todos_controller.rb
index 23868d986e9626975f78010333b36090430d741a..5685d0f4e7c892d30cfb97a543065814cb4fa8e3 100644
--- a/app/controllers/projects/todos_controller.rb
+++ b/app/controllers/projects/todos_controller.rb
@@ -5,7 +5,7 @@ class Projects::TodosController < Projects::ApplicationController
     todo = TodoService.new.mark_todo(issuable, current_user)
 
     render json: {
-      count: current_user.todos_pending_count,
+      count: TodosFinder.new(current_user, state: :pending).execute.count,
       delete_path: dashboard_todo_path(todo)
     }
   end
diff --git a/app/finders/todos_finder.rb b/app/finders/todos_finder.rb
index 7806d9e4cc50fe46d9f7e599ef62f28896f8fbd0..ff866c2faa502e219f8bb06603dfa72ddb997c60 100644
--- a/app/finders/todos_finder.rb
+++ b/app/finders/todos_finder.rb
@@ -8,7 +8,7 @@
 #     action_id: integer
 #     author_id: integer
 #     project_id; integer
-#     state: 'pending' or 'done'
+#     state: 'pending' (default) or 'done'
 #     type: 'Issue' or 'MergeRequest'
 #
 
@@ -37,7 +37,7 @@ class TodosFinder
   private
 
   def action_id?
-    action_id.present? && [Todo::ASSIGNED, Todo::MENTIONED, Todo::BUILD_FAILED, Todo::MARKED].include?(action_id.to_i)
+    action_id.present? && Todo::ACTION_NAMES.has_key?(action_id.to_i)
   end
 
   def action_id
diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb
index 950f323e383a94f128453a55ee92b5b4c1b8cf61..e12a10529881930a6921d9470fa76989e086b7b9 100644
--- a/app/helpers/appearances_helper.rb
+++ b/app/helpers/appearances_helper.rb
@@ -31,7 +31,7 @@ module AppearancesHelper
     end
   end
 
-  def navbar_icon(icon_name, size: 16)
+  def custom_icon(icon_name, size: 16)
     render "shared/icons/#{icon_name}.svg", size: size
   end
 end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 428a42266d0e12ec484a5b78b0be596ad0acce83..abe115d8c68be2b47ce43cf21bf958da62e8ab34 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -1,10 +1,7 @@
 module BlobHelper
-  def highlighter(blob_name, blob_content, repository: nil, nowrap: false)
-    Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap, repository: repository)
-  end
-
-  def highlight(blob_name, blob_content, repository: nil, nowrap: false, plain: false)
-    Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain, repository: repository)
+  def highlight(blob_name, blob_content, repository: nil, plain: false)
+    highlighted = Gitlab::Highlight.highlight(blob_name, blob_content, plain: plain, repository: repository)
+    raw %(<pre class="code highlight"><code>#{highlighted}</code></pre>)
   end
 
   def no_highlight_files
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index 0f097f86816555156cea7ca9db87042035fc537c..b478580978bfff79ec8df2190a4b5a3ea5e3599c 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -15,29 +15,13 @@ module ButtonHelper
   #
   # See http://clipboardjs.com/#usage
   def clipboard_button(data = {})
+    data = { toggle: 'tooltip', placement: 'bottom', container: 'body' }.merge(data)
     content_tag :button,
       icon('clipboard'),
       class: "btn btn-clipboard",
       data: data,
-      type: :button
-  end
-
-  # Output a "Copy to Clipboard" button with a custom CSS class
-  #
-  # data - Data attributes passed to `content_tag`
-  # css_class - Class passed to the `content_tag`
-  #
-  # Examples:
-  # 
-  #   # Define the target element
-  #   clipboard_button_with_class({clipboard_target: "div#foo"}, css_class: "btn-clipboard")
-  #   # => "<button class='btn btn-clipboard' data-clipboard-target='div#foo'>...</button>"
-  def clipboard_button_with_class(data = {}, css_class: 'btn-clipboard')
-    content_tag :button,
-      icon('clipboard'),
-      class: "btn #{css_class}",
-      data: data,
-      type: :button
+      type: :button,
+      title: "Copy to Clipboard"
   end
 
   def http_clone_button(project, placement = 'right', append_link: true)
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
index 8e4ae1e6aec16c1b0ff1d1d15747b3bd06da6065..e6c99c9959e159ed78089c022ce869246f9b397c 100644
--- a/app/helpers/ci_status_helper.rb
+++ b/app/helpers/ci_status_helper.rb
@@ -29,8 +29,10 @@ module CiStatusHelper
         'check'
       when 'failed'
         'close'
-      when 'running', 'pending'
+      when 'pending'
         'clock-o'
+      when 'running'
+        'spinner'
       else
         'circle'
       end
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index eb57516247db444de262a6edf3139f72504ff929..adab901700cc17047ddffa7efbf00450ca826d82 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -8,6 +8,10 @@ module DiffHelper
     [marked_old_line, marked_new_line]
   end
 
+  def expand_all_diffs?
+    @expand_all_diffs || params[:expand_all_diffs].present?
+  end
+
   def diff_view
     diff_views = %w(inline parallel)
 
@@ -18,16 +22,14 @@ module DiffHelper
     end
   end
 
-  def diff_hard_limit_enabled?
-    params[:force_show_diff].present?
-  end
-
   def diff_options
-    options = { ignore_whitespace_change: hide_whitespace? }
-    if diff_hard_limit_enabled?
-      options.merge!(Commit.max_diff_options)
+    default_options = Commit.max_diff_options
+
+    if action_name == 'diff_for_path'
+      default_options[:paths] = params.values_at(:old_path, :new_path)
     end
-    options
+
+    default_options.merge(ignore_whitespace_change: hide_whitespace?)
   end
 
   def safe_diff_files(diffs, diff_refs: nil, repository: nil)
@@ -35,7 +37,7 @@ module DiffHelper
   end
 
   def unfold_bottom_class(bottom)
-    bottom ? 'js-unfold-bottom' : ''
+    bottom ? 'js-unfold js-unfold-bottom' : ''
   end
 
   def unfold_class(unfold)
@@ -90,7 +92,7 @@ module DiffHelper
 
   def commit_for_diff(diff_file)
     return diff_file.content_commit if diff_file.content_commit
-    
+
     if diff_file.deleted_file
       @base_commit || @commit.parent || @commit
     else
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 294b7e92b8ddb15f499b193441dfcdec19857fcf..47d174361dbfdfd55f786bfe479c7330c13f15db 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -61,7 +61,7 @@ module IssuablesHelper
     output = content_tag :strong, "#{text} #{issuable.to_reference}", class: "identifier"
     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, mobile_classes: "hidden-xs", tooltip: true)
       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
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index 4da1f4865a42b4ea62c8eaca6ef78c1c6d121851..db6e731c7446dde8a884c0b4bfe9d3eafad1deb8 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -96,4 +96,8 @@ module MergeRequestsHelper
       ["#{source_path}:#{source_branch}", "#{target_path}:#{target_branch}"]
     end
   end
+
+  def merge_request_button_visibility(merge_request, closed)
+    return 'hidden' if merge_request.closed? == closed || (merge_request.merged? == closed && !merge_request.closed?)
+  end
 end
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index 2302e65c5378952e834ed81c74f87f6c0349fcd0..98143dcee9b6b8fe0d993fe8e7d1c8413b7bb7d4 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -24,7 +24,15 @@ module NotesHelper
     }.to_json
   end
 
-  def link_to_new_diff_note(line_code, position, line_type = nil)
+  def diff_view_data
+    return {} unless @comments_target
+
+    @comments_target.slice(:noteable_id, :noteable_type, :commit_id)
+  end
+
+  def diff_view_line_data(line_code, position, line_type)
+    return if @diff_notes_disabled
+
     use_legacy_diff_note = @use_legacy_diff_notes
     # If the controller doesn't force the use of legacy diff notes, we
     # determine this on a line-by-line basis by seeing if there already exist
@@ -41,11 +49,8 @@ module NotesHelper
     end
 
     data = {
-      noteable_type: @comments_target[:noteable_type],
-      noteable_id:   @comments_target[:noteable_id],
-      commit_id:     @comments_target[:commit_id],
-      line_type:     line_type,
-      line_code:     line_code
+      line_code: line_code,
+      line_type: line_type,
     }
 
     if use_legacy_diff_note
@@ -73,11 +78,7 @@ module NotesHelper
       )
     end
 
-    button_tag(class: 'btn add-diff-note js-add-diff-note-button',
-               data: data,
-               title: 'Add a comment to this line') do
-      icon('comment-o')
-    end
+    data
   end
 
   def link_to_reply_discussion(note, line_type = nil)
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 3bbbb26cff2a3d86877857510d4e1be3aa13a148..a733dff1579d2dd1a4028bc24261613ae8fc5f07 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -19,7 +19,7 @@ module ProjectsHelper
   end
 
   def link_to_member(project, author, opts = {}, &block)
-    default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" }
+    default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name", tooltip: false }
     opts = default_opts.merge(opts)
 
     return "(deleted)" unless author
@@ -33,7 +33,8 @@ module ProjectsHelper
     if opts[:by_username]
       author_html << content_tag(:span, sanitize("@#{author.username}"), class: opts[:author_class]) if opts[:name]
     else
-      author_html << content_tag(:span, sanitize(author.name), class: opts[:author_class]) if opts[:name]
+      tooltip_data = { placement: 'top' }
+      author_html << content_tag(:span, sanitize(author.name), class: [opts[:author_class], ('has-tooltip' if opts[:tooltip])], title: (author.to_reference if opts[:tooltip]), data: (tooltip_data if opts[:tooltip])) if opts[:name]
     end
 
     author_html << capture(&block) if block
@@ -60,7 +61,7 @@ module ProjectsHelper
     project_link = link_to simple_sanitize(project.name), project_path(project), { class: "project-item-select-holder" }
 
     if current_user
-      project_link << icon("chevron-down", class: "dropdown-toggle-caret js-projects-dropdown-toggle", data: { target: ".js-dropdown-menu-projects", toggle: "dropdown" })
+      project_link << icon("chevron-down", class: "dropdown-toggle-caret js-projects-dropdown-toggle", aria: { label: "Toggle switch project dropdown" }, data: { target: ".js-dropdown-menu-projects", toggle: "dropdown" })
     end
 
     full_title = "#{namespace_link} / #{project_link}".html_safe
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index f9fc525df6fd8d81877fd37a34efff0f601bbb94..fcb2703e837a74a26af3f3a7298f8d7a1d87137b 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -43,15 +43,15 @@ module SearchHelper
   # Autocomplete results for internal help pages
   def help_autocomplete
     [
-      { category: "Help", label: "API Help",           url: help_page_path("api", "README") },
-      { category: "Help", label: "Markdown Help",      url: help_page_path("markdown", "markdown") },
-      { category: "Help", label: "Permissions Help",   url: help_page_path("permissions", "permissions") },
-      { category: "Help", label: "Public Access Help", url: help_page_path("public_access", "public_access") },
-      { category: "Help", label: "Rake Tasks Help",    url: help_page_path("raketasks", "README") },
-      { category: "Help", label: "SSH Keys Help",      url: help_page_path("ssh", "README") },
-      { category: "Help", label: "System Hooks Help",  url: help_page_path("system_hooks", "system_hooks") },
-      { category: "Help", label: "Webhooks Help",      url: help_page_path("web_hooks", "web_hooks") },
-      { category: "Help", label: "Workflow Help",      url: help_page_path("workflow", "README") },
+      { category: "Help", label: "API Help",           url: help_page_path("api/README") },
+      { category: "Help", label: "Markdown Help",      url: help_page_path("markdown/markdown") },
+      { category: "Help", label: "Permissions Help",   url: help_page_path("user/permissions") },
+      { category: "Help", label: "Public Access Help", url: help_page_path("public_access/public_access") },
+      { category: "Help", label: "Rake Tasks Help",    url: help_page_path("raketasks/README") },
+      { category: "Help", label: "SSH Keys Help",      url: help_page_path("ssh/README") },
+      { category: "Help", label: "System Hooks Help",  url: help_page_path("system_hooks/system_hooks") },
+      { category: "Help", label: "Webhooks Help",      url: help_page_path("web_hooks/web_hooks") },
+      { category: "Help", label: "Workflow Help",      url: help_page_path("workflow/README") },
     ]
   end
 
diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb
index a832a6c8df7144d68a1a0486aff5ae986296e7a5..e3a208f826a0fa8d5d1d165010057d9a4f0dbffc 100644
--- a/app/helpers/todos_helper.rb
+++ b/app/helpers/todos_helper.rb
@@ -1,10 +1,10 @@
 module TodosHelper
   def todos_pending_count
-    TodosFinder.new(current_user, state: :pending).execute.count
+    @todos_pending_count ||= TodosFinder.new(current_user, state: :pending).execute.count
   end
 
   def todos_done_count
-    TodosFinder.new(current_user, state: :done).execute.count
+    @todos_done_count ||= TodosFinder.new(current_user, state: :done).execute.count
   end
 
   def todo_action_name(todo)
@@ -13,6 +13,7 @@ module TodosHelper
     when Todo::MENTIONED then 'mentioned you on'
     when Todo::BUILD_FAILED then 'The build failed for your'
     when Todo::MARKED then 'added a todo for'
+    when Todo::APPROVAL_REQUIRED then 'set you as an approver for'
     end
   end
 
diff --git a/app/helpers/u2f_helper.rb b/app/helpers/u2f_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..143b4ca6b51a883278c786ddc12ea4677933dc23
--- /dev/null
+++ b/app/helpers/u2f_helper.rb
@@ -0,0 +1,5 @@
+module U2fHelper
+  def inject_u2f_api?
+    browser.chrome? && browser.version.to_i >= 41 && !browser.device.mobile?
+  end
+end
diff --git a/app/helpers/workhorse_helper.rb b/app/helpers/workhorse_helper.rb
index 65598ad9ed329937f52275e6791f36715098ddd6..d887cdadc3475a731a3b414528b33ec512a1d1ad 100644
--- a/app/helpers/workhorse_helper.rb
+++ b/app/helpers/workhorse_helper.rb
@@ -28,4 +28,10 @@ module WorkhorseHelper
     headers.store(*Gitlab::Workhorse.send_git_archive(repository, ref: ref, format: format))
     head :ok
   end
+
+  # Send an entry from artifacts through Workhorse
+  def send_artifacts_entry(build, entry)
+    headers.store(*Gitlab::Workhorse.send_artifacts_entry(build, entry))
+    head :ok
+  end
 end
diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb
index 236b6ab00d8f778f1425ea6e12f8ce7054cd277c..e0af70814114e8fed0136ae01d78ba9caf8eb58e 100644
--- a/app/mailers/emails/projects.rb
+++ b/app/mailers/emails/projects.rb
@@ -29,7 +29,8 @@ module Emails
       # used in notify layout
       @target_url = @message.target_url
       @project = Project.find(project_id)
-      
+      @diff_notes_disabled = true
+
       add_project_headers
       headers['X-GitLab-Author'] = @message.author_username
 
diff --git a/app/models/ability.rb b/app/models/ability.rb
index eeb0ceba08113b9d385ee63cbfb8b3cf30dc3dc9..6fd18f2ee24bd0dea3b42d0aff4c9317eeac8534 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -204,7 +204,8 @@ class Ability
         :download_code,
         :fork_project,
         :read_commit_status,
-        :read_pipeline
+        :read_pipeline,
+        :read_container_image
       ]
     end
 
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index e189dbac2856dbbd26f35129b2fb2d7aaf60fb3a..ffac3a22efc09eab3cd818fa82233bed8b236cc0 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -5,6 +5,7 @@ module Ci
     belongs_to :erased_by, class_name: 'User'
 
     serialize :options
+    serialize :yaml_variables
 
     validates :coverage, numericality: true, allow_blank: true
     validates_presence_of :ref
@@ -52,7 +53,10 @@ module Ci
         new_build.stage = build.stage
         new_build.stage_idx = build.stage_idx
         new_build.trigger_request = build.trigger_request
+        new_build.yaml_variables = build.yaml_variables
+        new_build.when = build.when
         new_build.user = user
+        new_build.environment = build.environment
         new_build.save
         MergeRequests::AddTodoWhenBuildFailsService.new(build.project, nil).close(new_build)
         new_build
@@ -117,7 +121,12 @@ module Ci
     end
 
     def variables
-      predefined_variables + yaml_variables + project_variables + trigger_variables
+      variables = []
+      variables += predefined_variables
+      variables += yaml_variables if yaml_variables
+      variables += project_variables
+      variables += trigger_variables
+      variables
     end
 
     def merge_request
@@ -394,30 +403,6 @@ module Ci
       self.update(erased_by: user, erased_at: Time.now, artifacts_expire_at: nil)
     end
 
-    def yaml_variables
-      global_yaml_variables + job_yaml_variables
-    end
-
-    def global_yaml_variables
-      if pipeline.config_processor
-        pipeline.config_processor.global_variables.map do |key, value|
-          { key: key, value: value, public: true }
-        end
-      else
-        []
-      end
-    end
-
-    def job_yaml_variables
-      if pipeline.config_processor
-        pipeline.config_processor.job_variables(name).map do |key, value|
-          { key: key, value: value, public: true }
-        end
-      else
-        []
-      end
-    end
-
     def project_variables
       project.variables.map do |variable|
         { key: variable.key, value: variable.value, public: false }
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 431a91004cdafe8efc3b8fccb62fa301c580aefd..a8e6a23e1c48eed7f438fa9bfb7d854718bec940 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -6,6 +6,8 @@ module Ci
     self.table_name = 'ci_commits'
 
     belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
+    belongs_to :user
+
     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
@@ -223,6 +225,8 @@ module Ci
     end
 
     def keep_around_commits
+      return unless project
+      
       project.repository.keep_around(self.sha)
       project.repository.keep_around(self.before_sha)
     end
diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb
index 06beff177b17fa371edd628f0c7f96120a8d9266..800a16ab246c83db03d993b6b45154bab80ab987 100644
--- a/app/models/concerns/awardable.rb
+++ b/app/models/concerns/awardable.rb
@@ -65,8 +65,7 @@ module Awardable
 
   def create_award_emoji(name, current_user)
     return unless emoji_awardable?
-
-    award_emoji.create(name: name, user: current_user)
+    award_emoji.create(name: normalize_name(name), user: current_user)
   end
 
   def remove_award_emoji(name, current_user)
@@ -80,4 +79,10 @@ module Awardable
       create_award_emoji(emoji_name, current_user)
     end
   end
+
+  private
+
+  def normalize_name(name)
+    Gitlab::AwardEmoji.normalize_emoji_name(name)
+  end
 end
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index 8cac47246db63cfd6c3bd9bedb38c8ff56f5bc76..ec9e0f1b1d0a6537b45bd9131577204198abf8da 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -14,14 +14,14 @@ module Mentionable
       attr = attr.to_s
       mentionable_attrs << [attr, options]
     end
+  end
 
+  included do
     # Accessor for attributes marked mentionable.
-    def mentionable_attrs
-      @mentionable_attrs ||= []
+    cattr_accessor :mentionable_attrs, instance_accessor: false do
+      []
     end
-  end
 
-  included do
     if self < Participable
       participant -> (user, ext) { all_references(user, extractor: ext) }
     end
diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb
index 9822844357dde157dc616c078a81de29b30fed83..70740c76e43ecc01ffc3234c065ae9b0c7f543be 100644
--- a/app/models/concerns/participable.rb
+++ b/app/models/concerns/participable.rb
@@ -41,9 +41,12 @@ module Participable
     def participant(attr)
       participant_attrs << attr
     end
+  end
 
-    def participant_attrs
-      @participant_attrs ||= []
+  included do
+    # Accessor for participant attributes.
+    cattr_accessor :participant_attrs, instance_accessor: false do
+      []
     end
   end
 
diff --git a/app/models/label.rb b/app/models/label.rb
index dc5586f57566ff5c93de6262e317d559ee887dbe..35e678001dc6c29765fa987a23d5ae45ce202139 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -52,14 +52,17 @@ class Label < ActiveRecord::Base
   # This pattern supports cross-project references.
   #
   def self.reference_pattern
+    # NOTE: The id pattern only matches when all characters on the expression
+    # are digits, so it will match ~2 but not ~2fa because that's probably a
+    # label name and we want it to be matched as such.
     @reference_pattern ||= %r{
       (#{Project.reference_pattern})?
       #{Regexp.escape(reference_prefix)}
       (?:
-        (?<label_id>\d+) | # Integer-based label ID, or
+        (?<label_id>\d+(?!\S\w)\b) | # Integer-based label ID, or
         (?<label_name>
-          [A-Za-z0-9_\-\?&]+ | # String-based single-word label title, or
-          "[^,]+"              # String-based multi-word label surrounded in quotes
+          [A-Za-z0-9_\-\?\.&]+ | # String-based single-word label title, or
+          ".+?"                  # String-based multi-word label surrounded in quotes
         )
       )
     }x
diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb
index 790dfd4d4804fb6ea2c9b5c13465a144be3fb144..04a651d50abd4e99c5b6326f2bab10e4e23c219b 100644
--- a/app/models/legacy_diff_note.rb
+++ b/app/models/legacy_diff_note.rb
@@ -38,7 +38,7 @@ class LegacyDiffNote < Note
   end
 
   def diff_line
-    @diff_line ||= diff_file.line_for_line_code(self.line_code)
+    @diff_line ||= diff_file.line_for_line_code(self.line_code) if diff_file
   end
 
   def for_line?(line)
@@ -55,7 +55,7 @@ class LegacyDiffNote < Note
   def active?
     return @active if defined?(@active)
     return true if for_commit?
-    return true unless self.diff
+    return true unless diff_line
     return false unless noteable
 
     noteable_diff = find_noteable_diff
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 393d8a7265796752d0b2d1aa472ce40ec0f49f8a..157901378d32167ee1e84a90e3ec46e826b8d354 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -19,7 +19,7 @@ class MergeRequest < ActiveRecord::Base
   after_create :create_merge_request_diff, unless: :importing?
   after_update :update_merge_request_diff
 
-  delegate :commits, :diffs, :real_size, to: :merge_request_diff, prefix: nil
+  delegate :commits, :real_size, to: :merge_request_diff, prefix: nil
 
   # When this attribute is true some MR validation is ignored
   # It allows us to close or modify broken merge requests
@@ -164,6 +164,10 @@ class MergeRequest < ActiveRecord::Base
     merge_request_diff ? merge_request_diff.first_commit : compare_commits.first
   end
 
+  def diffs(*args)
+    merge_request_diff ? merge_request_diff.diffs(*args) : compare.diffs(*args)
+  end
+
   def diff_size
     merge_request_diff.size
   end
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index ba235750aeb9e5cf8b9f13c2bf2b42725c4b2726..feaba925bad1197818dd18d623d0a2013b9e48c3 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -46,7 +46,8 @@ class MergeRequestDiff < ActiveRecord::Base
         compare.diffs(options)
       end
     else
-      @diffs ||= load_diffs(st_diffs, options)
+      @diffs ||= {}
+      @diffs[options] ||= load_diffs(st_diffs, options)
     end
   end
 
@@ -144,6 +145,12 @@ class MergeRequestDiff < ActiveRecord::Base
 
   def load_diffs(raw, options)
     if raw.respond_to?(:each)
+      if paths = options[:paths]
+        raw = raw.select do |diff|
+          paths.include?(diff[:old_path]) || paths.include?(diff[:new_path])
+        end
+      end
+
       Gitlab::Git::DiffCollection.new(raw, options)
     else
       Gitlab::Git::DiffCollection.new([])
diff --git a/app/models/note.rb b/app/models/note.rb
index ffffd0c0838006c587346c2377de11410372bdb5..0ce10c77de92dacd1ff627ad3fb246d96e94fff1 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -10,6 +10,10 @@ class Note < ActiveRecord::Base
   # Banzai::ObjectRenderer.
   attr_accessor :note_html
 
+  # An Array containing the number of visible references as generated by
+  # Banzai::ObjectRenderer
+  attr_accessor :user_visible_reference_count
+
   default_value_for :system, false
 
   attr_mentionable :note, pipeline: :note
@@ -193,7 +197,15 @@ class Note < ActiveRecord::Base
   end
 
   def cross_reference_not_visible_for?(user)
-    cross_reference? && referenced_mentionables(user).empty?
+    cross_reference? && !has_referenced_mentionables?(user)
+  end
+
+  def has_referenced_mentionables?(user)
+    if user_visible_reference_count.present?
+      user_visible_reference_count > 0
+    else
+      referenced_mentionables(user).any?
+    end
   end
 
   def award_emoji?
@@ -217,8 +229,7 @@ class Note < ActiveRecord::Base
   end
 
   def award_emoji_name
-    original_name = note.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1]
-    Gitlab::AwardEmoji.normalize_emoji_name(original_name)
+    note.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1]
   end
 
   private
diff --git a/app/models/project.rb b/app/models/project.rb
index d26aa8073e8fb3760cf0186593a6b138995a4a8c..12851c5d0ec52ce7071b08f2a52b6536cece2e5a 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -470,8 +470,8 @@ class Project < ActiveRecord::Base
     return super(value) unless Gitlab::UrlSanitizer.valid?(value)
 
     import_url = Gitlab::UrlSanitizer.new(value)
-    create_or_update_import_data(credentials: import_url.credentials)
     super(import_url.sanitized_url)
+    create_or_update_import_data(credentials: import_url.credentials)
   end
 
   def import_url
@@ -483,7 +483,13 @@ class Project < ActiveRecord::Base
     end
   end
 
+  def valid_import_url?
+    valid? || errors.messages[:import_url].nil?
+  end
+
   def create_or_update_import_data(data: nil, credentials: nil)
+    return unless import_url.present? && valid_import_url?
+
     project_import_data = import_data || build_import_data
     if data
       project_import_data.data ||= {}
@@ -1038,8 +1044,8 @@ class Project < ActiveRecord::Base
     pipelines.order(id: :desc).find_by(sha: sha, ref: ref)
   end
 
-  def ensure_pipeline(sha, ref)
-    pipeline(sha, ref) || pipelines.create(sha: sha, ref: ref)
+  def ensure_pipeline(sha, ref, current_user = nil)
+    pipeline(sha, ref) || pipelines.create(sha: sha, ref: ref, user: current_user)
   end
 
   def enable_ci
diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb
index 016172c6d7ee9787164ec1d9097262792fa94714..f4bcb49b34d5770ac5c0e5ccaac01b8703f6a385 100644
--- a/app/models/sent_notification.rb
+++ b/app/models/sent_notification.rb
@@ -72,6 +72,19 @@ class SentNotification < ActiveRecord::Base
     end
   end
 
+  def position=(new_position)
+    if new_position.is_a?(String)
+      new_position = JSON.parse(new_position) rescue nil
+    end
+
+    if new_position.is_a?(Hash)
+      new_position = new_position.with_indifferent_access
+      new_position = Gitlab::Diff::Position.new(new_position)
+    end
+
+    super(new_position)
+  end
+
   def to_param
     self.reply_key
   end
diff --git a/app/models/todo.rb b/app/models/todo.rb
index ac3fdbc7f3baab63054af7ffbfee7064b87d8d36..8d7a5965aa15f4698fc869c3b39a3edadd39601c 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -1,14 +1,16 @@
 class Todo < ActiveRecord::Base
-  ASSIGNED     = 1
-  MENTIONED    = 2
-  BUILD_FAILED = 3
-  MARKED       = 4
+  ASSIGNED          = 1
+  MENTIONED         = 2
+  BUILD_FAILED      = 3
+  MARKED            = 4
+  APPROVAL_REQUIRED = 5 # This is an EE-only feature
 
   ACTION_NAMES = {
     ASSIGNED => :assigned,
     MENTIONED => :mentioned,
     BUILD_FAILED => :build_failed,
-    MARKED => :marked
+    MARKED => :marked,
+    APPROVAL_REQUIRED => :approval_required
   }
 
   belongs_to :author, class_name: "User"
diff --git a/app/models/user.rb b/app/models/user.rb
index 79c670cb35ac5fd89cbb350b2d057ef1bff5e2bb..3d0a033785caf04ca5e4fcee235dc9c2fea3dc2d 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -85,9 +85,10 @@ class User < ActiveRecord::Base
   has_one  :abuse_report,             dependent: :destroy
   has_many :spam_logs,                dependent: :destroy
   has_many :builds,                   dependent: :nullify, class_name: 'Ci::Build'
+  has_many :pipelines,                dependent: :nullify, class_name: 'Ci::Pipeline'
   has_many :todos,                    dependent: :destroy
   has_many :notification_settings,    dependent: :destroy
-  has_many :award_emoji,              as: :awardable, dependent: :destroy
+  has_many :award_emoji,              dependent: :destroy
 
   #
   # Validations
diff --git a/app/services/ci/create_builds_service.rb b/app/services/ci/create_builds_service.rb
index 2dcb052d274e25513203989c509582900a7ff18f..3b21f0acb966c9f1d9715a50891ad09f76ff34b4 100644
--- a/app/services/ci/create_builds_service.rb
+++ b/app/services/ci/create_builds_service.rb
@@ -36,7 +36,9 @@ module Ci
                            :allow_failure,
                            :stage,
                            :stage_idx,
-                           :environment)
+                           :environment,
+                           :when,
+                           :yaml_variables)
 
         build_attrs.merge!(pipeline: @pipeline,
                            ref: @pipeline.ref,
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index b1ee68741902327a40c346f436d197147b7945d7..be91bf0db85a30408b71731674fdf4c02d3fb339 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -2,6 +2,7 @@ module Ci
   class CreatePipelineService < BaseService
     def execute
       pipeline = project.pipelines.new(params)
+      pipeline.user = current_user
 
       unless ref_names.include?(params[:ref])
         pipeline.errors.add(:base, 'Reference not found')
diff --git a/app/services/compare_service.rb b/app/services/compare_service.rb
index e2bccbdbcc3a19a16775ac78a62099888b1509e3..149822aa64743ad5637c2058893b6c444d4b7601 100644
--- a/app/services/compare_service.rb
+++ b/app/services/compare_service.rb
@@ -3,7 +3,7 @@ require 'securerandom'
 # Compare 2 branches for one repo or between repositories
 # and return Gitlab::Git::Compare object that responds to commits and diffs
 class CompareService
-  def execute(source_project, source_branch, target_project, target_branch, diff_options = {})
+  def execute(source_project, source_branch, target_project, target_branch)
     source_commit = source_project.commit(source_branch)
     return unless source_commit
 
diff --git a/app/services/create_commit_builds_service.rb b/app/services/create_commit_builds_service.rb
index f947e8f452eafb64cd82380fd7249ab30c8298c6..0b66b854deabd245b02259f8cb9cd7e3c424176d 100644
--- a/app/services/create_commit_builds_service.rb
+++ b/app/services/create_commit_builds_service.rb
@@ -14,7 +14,13 @@ class CreateCommitBuildsService
       return false
     end
 
-    @pipeline = Ci::Pipeline.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,
+      user: user)
 
     ##
     # Skip creating pipeline if no gitlab-ci.yml is found
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index 02fca5c0ea30c4486d820a459b2ce196aa383b4e..18971bd0be3eec0572bbf2b23b8a9a5570f52012 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -8,7 +8,6 @@ module Notes
       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
 
diff --git a/app/services/projects/housekeeping_service.rb b/app/services/projects/housekeeping_service.rb
index 752c11d7ae65e3647c4315bc551a9769c82d5002..29b3981f49f76f1f3b824f56bd820869cec8c65b 100644
--- a/app/services/projects/housekeeping_service.rb
+++ b/app/services/projects/housekeeping_service.rb
@@ -7,8 +7,6 @@
 #
 module Projects
   class HousekeepingService < BaseService
-    include Gitlab::ShellAdapter
-
     LEASE_TIMEOUT = 3600
 
     class LeaseTaken < StandardError
@@ -24,11 +22,7 @@ module Projects
     def execute
       raise LeaseTaken unless try_obtain_lease
 
-      GitlabShellOneShotWorker.perform_async(:gc, @project.repository_storage_path, @project.path_with_namespace)
-    ensure
-      Gitlab::Metrics.measure(:reset_pushes_since_gc) do
-        update_pushes_since_gc(0)
-      end
+      execute_gitlab_shell_gc
     end
 
     def needed?
@@ -36,19 +30,27 @@ module Projects
     end
 
     def increment!
-      Gitlab::Metrics.measure(:increment_pushes_since_gc) do
-        update_pushes_since_gc(@project.pushes_since_gc + 1)
+      if Gitlab::ExclusiveLease.new("project_housekeeping:increment!:#{@project.id}", timeout: 60).try_obtain
+        Gitlab::Metrics.measure(:increment_pushes_since_gc) do
+          update_pushes_since_gc(@project.pushes_since_gc + 1)
+        end
       end
     end
 
     private
 
-    def update_pushes_since_gc(new_value)
-      if Gitlab::ExclusiveLease.new("project_housekeeping:update_pushes_since_gc:#{project.id}", timeout: 60).try_obtain
-        @project.update_column(:pushes_since_gc, new_value)
+    def execute_gitlab_shell_gc
+      GitGarbageCollectWorker.perform_async(@project.id)
+    ensure
+      Gitlab::Metrics.measure(:reset_pushes_since_gc) do
+        update_pushes_since_gc(0)
       end
     end
 
+    def update_pushes_since_gc(new_value)
+      @project.update_column(:pushes_since_gc, new_value)
+    end
+
     def try_obtain_lease
       Gitlab::Metrics.measure(:obtain_housekeeping_lease) do
         lease = ::Gitlab::ExclusiveLease.new("project_housekeeping:#{@project.id}", timeout: LEASE_TIMEOUT)
diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb
index 6afc048576dfb0d399cff816cc1cc6731c347b2d..998789d64d226a987f7c48313e9af16bbe485c08 100644
--- a/app/services/projects/import_export/export_service.rb
+++ b/app/services/projects/import_export/export_service.rb
@@ -10,7 +10,7 @@ module Projects
 
       def save_all
         if [version_saver, project_tree_saver, uploads_saver, repo_saver, wiki_repo_saver].all?(&:save)
-          Gitlab::ImportExport::Saver.save(shared: @shared)
+          Gitlab::ImportExport::Saver.save(project: project, shared: @shared)
           notify_success
         else
           cleanup_and_notify
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index 163ebf26c84393982360b8f7652ae47d989bac3b..cdad0426b02692c0774591cb1416b079cde0693f 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -43,7 +43,7 @@ module Projects
     def import_repository
       begin
         gitlab_shell.import_repository(project.repository_storage_path, project.path_with_namespace, project.import_url)
-      rescue Gitlab::Shell::Error => e
+      rescue => e
         raise Error,  "Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}"
       end
     end
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index 6bb0a72d30ebd1ec8a60304940b5c8db5f18eae5..6b48d68cccb20c104017ee83fd66a5763fef64cd 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -194,7 +194,7 @@ class TodoService
   end
 
   def create_assignment_todo(issuable, author)
-    if issuable.assignee && issuable.assignee != author
+    if issuable.assignee
       attributes = attributes_for_todo(issuable.project, issuable, author, Todo::ASSIGNED)
       create_todos(issuable.assignee, attributes)
     end
@@ -239,7 +239,6 @@ class TodoService
   def filter_mentioned_users(project, target, author)
     mentioned_users = target.mentioned_users(author)
     mentioned_users = reject_users_without_access(mentioned_users, project, target)
-    mentioned_users.delete(author)
     mentioned_users.uniq
   end
 
diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml
index dc083e50178352185f73ecae02ccaec6cf60f111..92e2dae48420edae270b92e52166b3bc05261250 100644
--- a/app/views/admin/appearances/_form.html.haml
+++ b/app/views/admin/appearances/_form.html.haml
@@ -13,7 +13,7 @@
     .col-sm-10
       = f.text_area :description, class: "form-control", rows: 10
       .hint
-        Description parsed with #{link_to "GitLab Flavored Markdown", help_page_path('markdown', 'markdown'), target: '_blank'}.
+        Description parsed with #{link_to "GitLab Flavored Markdown", help_page_path('markdown/markdown'), target: '_blank'}.
   .form-group
     = f.label :logo, class: 'control-label'
     .col-sm-10
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index 8de28528cda4593e9bdd0cc44c708b8063a54bf4..538d8176ce7c28c1cc0af538f864c4aaa5a6b0b7 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -38,11 +38,11 @@
             = source
         %span.help-block#import-sources-help
           Enabled sources for code import during project creation. OmniAuth must be configured for GitHub
-          = link_to "(?)", help_page_path("integration", "github")
+          = link_to "(?)", help_page_path("integration/github")
           , Bitbucket
-          = link_to "(?)", help_page_path("integration", "bitbucket")
+          = link_to "(?)", help_page_path("integration/bitbucket")
           and GitLab.com
-          = link_to "(?)", help_page_path("integration", "gitlab")
+          = link_to "(?)", help_page_path("integration/gitlab")
     .form-group
       %label.control-label.col-sm-2 Enabled Git access protocols
       .col-sm-10
diff --git a/app/views/admin/builds/_build.html.haml b/app/views/admin/builds/_build.html.haml
index 967151bc33be19dc70c2fb8d14f62357735af802..ce818c30c3086ed6d17ccd0b501223794452d355 100644
--- a/app/views/admin/builds/_build.html.haml
+++ b/app/views/admin/builds/_build.html.haml
@@ -1,30 +1,40 @@
 - project = build.project
-%tr.build
+%tr.build.commit
   %td.status
     = ci_status_with_icon(build.status)
 
-  %td.build-link
-    - if can?(current_user, :read_build, build.project)
-      = link_to namespace_project_build_url(build.project.namespace, build.project, build) do
-        %strong Build ##{build.id}
-    - else
-      %strong Build ##{build.id}
+  %td
+    .branch-commit
+      - if can?(current_user, :read_build, build.project)
+        = link_to namespace_project_build_url(build.project.namespace, build.project, build) do
+          %span.build-link ##{build.id}
+      - else
+        %span.build-link ##{build.id}
 
-    - if build.stuck?
-      %i.fa.fa-warning.text-warning
+      - if build.stuck?
+        %i.fa.fa-warning.text-warning
 
-  %td
-    - if project
-      = link_to project.name_with_namespace, admin_namespace_project_path(project.namespace, project)
+      - if build.ref
+        = link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref), class: "monospace branch-name"
+      - else
+        .light none
+      = custom_icon("icon_commit")
 
-  %td
-    = link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace"
+      = link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace commit-id"
+
+      .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
 
   %td
-    - if build.ref
-      = link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref)
-    - else
-      .light none
+    - if project
+      = link_to project.name_with_namespace, admin_namespace_project_path(project.namespace, project)
 
   %td
     - if build.try(:runner)
@@ -36,22 +46,15 @@
     #{build.stage} / #{build.name}
 
   %td
-    - 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
-
-  %td.duration
     - if build.duration
-      #{duration_in_words(build.finished_at, build.started_at)}
+      %p.duration
+        = custom_icon("icon_timer")
+        = duration_in_numbers(build.finished_at, build.started_at)
 
-  %td.timestamp
     - if build.finished_at
-      %span #{time_ago_with_tooltip(build.finished_at)}
+      %p.finished-at
+        = icon("calendar")
+        %span #{time_ago_with_tooltip(build.finished_at)}
 
   - if defined?(coverage) && coverage
     %td.coverage
diff --git a/app/views/admin/builds/index.html.haml b/app/views/admin/builds/index.html.haml
index 1e60205f91a1e605540e9977d4d91c9c0f3d40ec..3d77634d8fafe6b9ce72261c28845377a5599795 100644
--- a/app/views/admin/builds/index.html.haml
+++ b/app/views/admin/builds/index.html.haml
@@ -10,15 +10,20 @@
           All
           %span.badge.js-totalbuilds-count= @all_builds.count(:id)
 
+      %li{class: ('active' if @scope == 'pending')}
+        = link_to admin_builds_path(scope: :pending) do
+          Pending
+          %span.badge= number_with_delimiter(@all_builds.pending.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))
+          %span.badge= number_with_delimiter(@all_builds.running.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))
+          %span.badge= number_with_delimiter(@all_builds.finished.count(:id))
 
     .nav-controls
       - if @all_builds.running_or_pending.any?
@@ -27,7 +32,7 @@
   .row-content-block.second-block
     #{(@scope || 'all').capitalize} builds
 
-  %ul.content-list
+  %ul.content-list.builds-content-list
     - if @builds.blank?
       %li
         .nothing-here-block No builds to show
@@ -37,15 +42,11 @@
           %thead
             %tr
               %th Status
-              %th Build ID
-              %th Project
               %th Commit
-              %th Ref
+              %th Project
               %th Runner
               %th Name
-              %th Tags
-              %th Duration
-              %th Finished at
+              %th
               %th
 
           - @builds.each do |build|
diff --git a/app/views/admin/deploy_keys/new.html.haml b/app/views/admin/deploy_keys/new.html.haml
index 15aa059c93d0b5f82a329537e92a02d7189fd452..5c410a695bf12457b1402ab5f7e1a8ab0a6540c3 100644
--- a/app/views/admin/deploy_keys/new.html.haml
+++ b/app/views/admin/deploy_keys/new.html.haml
@@ -14,7 +14,7 @@
       .col-sm-10
         %p.light
           Paste a machine public key here. Read more about how to generate it
-          = link_to "here", help_page_path("ssh", "README")
+          = link_to "here", help_page_path("ssh/README")
         = f.text_area :key, class: "form-control thin_area", rows: 5
 
     .form-actions
diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml
index 59fd6c3fea0894ab02c6764ca84f41a78fa496fe..77a11e49e20088e4a73a2fc2024fa0c84cf05ac1 100644
--- a/app/views/admin/groups/_group.html.haml
+++ b/app/views/admin/groups/_group.html.haml
@@ -1,20 +1,26 @@
-- css_class = '' unless local_assigns[:css_class]
+- css_class = 'no-description' if group.description.blank?
 
-%li.group-row.group-admin{ class: css_class }
-  .group-avatar
-    = image_tag group_icon(group), class: 'avatar hidden-xs'
-  .group-details
-    .title
-      = link_to [:admin, group], class: 'group-name' do
-        = group.name
-    .group-stats
-      %span>= pluralize(number_with_delimiter(group.projects.count), 'project')
-      ,
-      %span= pluralize(number_with_delimiter(group.users.count), 'member')
-
-    - if group.description.present?
-      .description
-        = markdown(group.description, pipeline: :description)
-  .group-controls.hidden-xs
+%li.group-row{ class: css_class }
+  .controls
     = link_to 'Edit', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: 'btn'
     = link_to 'Delete', [:admin, group], data: { confirm: "Are you sure you want to remove #{group.name}?" }, method: :delete, class: 'btn btn-remove'
+  .stats
+    %span
+      = icon('bookmark')
+      = number_with_delimiter(group.projects.count)
+
+    %span
+      = icon('users')
+      = number_with_delimiter(group.users.count)
+
+    %span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)}
+      = visibility_level_icon(group.visibility_level, fw: false)
+
+  = image_tag group_icon(group), class: "avatar s40 hidden-xs"
+  .title
+    = link_to [:admin, group], class: 'group-name' do
+      = group.name
+
+  - if group.description.present?
+    .description
+      = markdown(group.description, pipeline: :description)
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 522153b37e3d30aab8aa2cd79a85caa80193c408..bb37469440076243a655866968de2f41a1a81c01 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -79,7 +79,7 @@
         .panel-body.form-holder
           %p.light
             Read more about project permissions
-            %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
+            %strong= link_to "here", help_page_path("user/permissions"), class: "vlink"
 
           = form_tag members_update_admin_group_path(@group), id: "new_project_member", class: "bulk_import", method: :put  do
             %div
diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml
index 7b388cf7862dcfeecc0df872943ac156ffe49c40..c217490963f1215cddf3579526cb6d5abe2c0530 100644
--- a/app/views/admin/hooks/index.html.haml
+++ b/app/views/admin/hooks/index.html.haml
@@ -3,7 +3,7 @@
   System hooks
 
 %p.light
-  #{link_to "System hooks ", help_page_path("system_hooks", "system_hooks"), class: "vlink"} can be
+  #{link_to "System hooks ", help_page_path("system_hooks/system_hooks"), class: "vlink"} can be
   used for binding events when GitLab creates a User or Project.
 
 %hr
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index 7fbce25b2c4351d28ac48b23668a1c4036b6a456..1e755785d907f46ba6871ac399ac24250fd994ee 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -66,7 +66,7 @@
       %ul.projects-list.content-list
         - @projects.each_with_index do |project|
           %li.project-row
-            .controls.pull-right
+            .controls
               - if project.archived
                 %span.label.label-warning archived
               %span.label.label-gray
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index 2c5aba7169938ed99de898e46a6902509820e209..b2c607361b3d417e9b9755efc11893abcda1a69d 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -132,7 +132,7 @@
               - else
                 passed.
 
-            = link_to icon('question-circle'), help_page_path('administration', 'repository_checks')
+            = link_to icon('question-circle'), help_page_path('administration/repository_checks')
 
           .form-group
             = f.submit 'Trigger repository check', class: 'btn btn-primary'
diff --git a/app/views/admin/users/_user.html.haml b/app/views/admin/users/_user.html.haml
index d3519f616f6c901d75e23606a7aa9ce7834ae62a..4bf1c9cde3ce6bb052537c32e9813b465a525078 100644
--- a/app/views/admin/users/_user.html.haml
+++ b/app/views/admin/users/_user.html.haml
@@ -14,7 +14,7 @@
       %span It's you!
     .user-email
       = mail_to user.email, user.email
-  .controls.pull-right
+  .controls
     = link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: 'btn'
     - unless user == current_user
       .dropdown.inline
diff --git a/app/views/dashboard/projects/_zero_authorized_projects.html.haml b/app/views/dashboard/projects/_zero_authorized_projects.html.haml
index d54c7cad7be48148d9caccb2d0d010c442f6c014..fdea834ff45954c8ac106e0e617f1bafe5b24a65 100644
--- a/app/views/dashboard/projects/_zero_authorized_projects.html.haml
+++ b/app/views/dashboard/projects/_zero_authorized_projects.html.haml
@@ -1,53 +1,46 @@
 - publicish_project_count = ProjectsFinder.new.execute(current_user).count
-%h3.page-title Welcome to GitLab!
-%p.light Self hosted Git management application.
-%hr
-%div
-  .dashboard-intro-icon
-    %i.fa.fa-bookmark-o
-  .dashboard-intro-text
-    %p.slead
-      You don't have access to any projects right now.
-      %br
-      - if current_user.can_create_project?
-        You can create up to
-        %strong= pluralize(number_with_delimiter(current_user.projects_limit), "project") + "."
-      - else
-        If you are added to a project, it will be displayed here.
-
+.blank-state.blank-state-welcome
+  %h2.blank-state-welcome-title
+    Welcome to GitLab
+  %p.blank-state-text
+    Code, test, and deploy together
+.blank-state
+  .blank-state-icon
+    = custom_icon("project", size: 50)
+  %h3.blank-state-title
+    You don't have access to any projects right now
+  %p.blank-state-text
     - if current_user.can_create_project?
-      .link_holder
-        = link_to new_project_path, class: "btn btn-new" do
-          = icon('plus')
-          New Project
+      You can create up to
+      %strong= number_with_delimiter(current_user.projects_limit)
+      = succeed "." do
+        = "project".pluralize(current_user.projects_limit)
+    - else
+      If you are added to a project, it will be displayed here.
+  - if current_user.can_create_project?
+    = link_to new_project_path, class: "btn btn-new" do
+      New project
 
 - if current_user.can_create_group?
-  %hr
-  %div
-    .dashboard-intro-icon
-      %i.fa.fa-users
-    .dashboard-intro-text
-      %p.slead
-        You can create a group for several dependent projects.
-        %br
-        Groups are the best way to manage projects and members.
-      .link_holder
-        = link_to new_group_path, class: "btn btn-new" do
-          %i.fa.fa-plus
-          New Group
+  .blank-state
+    .blank-state-icon
+      = custom_icon("group", size: 50)
+    %h3.blank-state-title
+      You can create a group for several dependent projects.
+    %p.blank-state-text
+      Groups are the best way to manage projects and members.
+    = link_to new_group_path, class: "btn btn-new" do
+      New group
 
 -if publicish_project_count > 0
-  %hr
-  %div
-    .dashboard-intro-icon
-      %i.fa.fa-globe
-    .dashboard-intro-text
-      %p.slead
-        There are
-        %strong= number_with_delimiter(publicish_project_count)
-        public projects on this server.
-        %br
-        Public projects are an easy way to allow everyone to have read-only access.
-      .link_holder
-        = link_to trending_explore_projects_path, class: "btn btn-new" do
-          Browse public projects
+  .blank-state
+    .blank-state-icon
+      = icon("globe")
+    %h3.blank-state-title
+      There are
+      = number_with_delimiter(publicish_project_count)
+      public projects on this server.
+    %p.blank-state-text
+      Public projects are an easy way to allow everyone to have read-only access.
+    = link_to trending_explore_projects_path, class: "btn btn-new" do
+      Browse projects
diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml
index 4565e752c1fdba955b2de712d41982dad82d9730..4f36a4a1c739f94629b0dd004249f390f1ce71e0 100644
--- a/app/views/dashboard/projects/index.html.haml
+++ b/app/views/dashboard/projects/index.html.haml
@@ -5,7 +5,8 @@
 - page_title    "Projects"
 - header_title  "Projects", dashboard_projects_path
 
-= render 'dashboard/projects_head'
+- if @projects.any? || params[:filter_projects]
+  = render 'dashboard/projects_head'
 
 - if @last_push
   = render "events/event_last_push", event: @last_push
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index fc42e5dcc661c84444d18e383d6c95137cbf2556..4e340b6ec16f3dad72ee06eb91ab93c8a717af5b 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -9,14 +9,14 @@
         %span
           To do
         %span.badge
-          = todos_pending_count
+          = number_with_delimiter(todos_pending_count)
     - todo_done_active = ('active' if params[:state] == 'done')
     %li{class: "todos-done #{todo_done_active}"}
       = link_to todos_filter_path(state: 'done') do
         %span
           Done
         %span.badge
-          = todos_done_count
+          = number_with_delimiter(todos_done_count)
 
   .nav-controls
     - if @todos.any?(&:pending?)
diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml
index a373f61bd3c7bd734eab5c67d63be20ee5c6384d..4debd3d608f822dcaecd7c8cea1dabd0bf54511a 100644
--- a/app/views/devise/sessions/two_factor.html.haml
+++ b/app/views/devise/sessions/two_factor.html.haml
@@ -1,3 +1,7 @@
+- if inject_u2f_api?
+  - content_for :page_specific_javascripts do
+    = page_specific_javascript_tag('u2f.js')
+
 %div
   .login-box
     .login-heading
diff --git a/app/views/errors/access_denied.html.haml b/app/views/errors/access_denied.html.haml
index 012e9857642ca898cec37e0f05b4f14e78fedf52..c034bbe430ef69a8bb6ab24d99232e28ef19e8d8 100644
--- a/app/views/errors/access_denied.html.haml
+++ b/app/views/errors/access_denied.html.haml
@@ -3,4 +3,4 @@
 %h3 Access Denied
 %hr
 %p You are not allowed to access this page.
-%p Read more about project permissions #{link_to "here", help_page_path("permissions", "permissions"), class: "vlink"}
+%p Read more about project permissions #{link_to "here", help_page_path("user/permissions"), class: "vlink"}
diff --git a/app/views/groups/group_members/_new_group_member.html.haml b/app/views/groups/group_members/_new_group_member.html.haml
index e7ab4f2409bb941ffb9bd020891200fb7496e53f..9bb9f96217770455302e3e0b5a0022f442ccaf27 100644
--- a/app/views/groups/group_members/_new_group_member.html.haml
+++ b/app/views/groups/group_members/_new_group_member.html.haml
@@ -12,7 +12,7 @@
       = select_tag :access_level, options_for_select(GroupMember.access_level_roles, @group_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"
+        %strong= link_to "here", help_page_path("user/permissions"), class: "vlink"
 
   .form-actions
     = f.submit 'Add users to group', class: "btn btn-create"
diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml
index 121a7de3ad78647e5bf3323011931bfbd37de388..a8fdbd8c426503eb4d44c73b7bbb70fbaa9e3dd4 100644
--- a/app/views/groups/milestones/index.html.haml
+++ b/app/views/groups/milestones/index.html.haml
@@ -6,7 +6,6 @@
   .nav-controls
     - if can?(current_user, :admin_milestones, @group)
       = link_to new_group_milestone_path(@group), class: "btn btn-new" do
-        = icon('plus')
         New Milestone
 
 .row-content-block
diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml
index c2f2d9912f78cfbbefc68a174b3803cba98eef38..33fee334d93f805e6f602ccbbe624be77a43b95b 100644
--- a/app/views/groups/projects.html.haml
+++ b/app/views/groups/projects.html.haml
@@ -7,7 +7,6 @@
     - if can? current_user, :admin_group, @group
       .controls
         = link_to new_project_path(namespace_id: @group.id), class: "btn btn-sm btn-success" do
-          = icon('plus')
           New Project
   %ul.well-list
     - @projects.each do |project|
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index a83eb7e88bb3a001e2eb90807742df5808d74570..eddeae98bc4edf86afc2d46cda70ac3fa503e0cb 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -6,8 +6,7 @@
 
 .cover-block.groups-cover-block
   %div{ class: container_class }
-    = link_to group_icon(@group), target: '_blank' do
-      = image_tag group_icon(@group), class: "avatar group-avatar s70"
+    = image_tag group_icon(@group), class: "avatar group-avatar s70"
     .group-info
       .cover-title
         %h1
diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml
index 8cc0b59edebd8c2545d4f3f2aaeefd4cb5a8c5c0..ce4536ebdc630fb09eeb0a100bdaa8c38e9fe073 100644
--- a/app/views/help/_shortcuts.html.haml
+++ b/app/views/help/_shortcuts.html.haml
@@ -18,6 +18,10 @@
                 %td.shortcut
                   .key s
                 %td Focus Search
+              %tr
+                %td.shortcut
+                  .key f
+                %td Focus Filter
               %tr
                 %td.shortcut
                   .key ?
diff --git a/app/views/help/show.html.haml b/app/views/help/show.html.haml
index 0398afb4c1d92968a06ad4060a6e69e22863d89a..be257b51b9ee2414ae5c303bcae85bfd12f34d8d 100644
--- a/app/views/help/show.html.haml
+++ b/app/views/help/show.html.haml
@@ -1,3 +1,3 @@
-- page_title @file.humanize, *@category.split("/").reverse.map(&:humanize)
+- page_title @path.split("/").reverse.map(&:humanize)
 .documentation.wiki
   = markdown @markdown.gsub('$your_email', current_user.try(:email) || "email@example.com")
diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml
index d676bc28c89dcaa71f298d60bb58278a18287273..431d312b4ca3c0b21d97642def227d62ad256b31 100644
--- a/app/views/help/ui.html.haml
+++ b/app/views/help/ui.html.haml
@@ -549,4 +549,4 @@
     %li wiki page
     %li help page
 
-  You can check how markdown rendered at #{link_to 'Markdown help page', help_page_path("markdown", "markdown")}.
+  You can check how markdown rendered at #{link_to 'Markdown help page', help_page_path("markdown/markdown")}.
diff --git a/app/views/import/github/new.html.haml b/app/views/import/github/new.html.haml
index 435ed7bd4cb5c7b339a9cf5112f06dae345e9fbe..4c6af0b7908fce8ea9db1984d745f39a13f26222 100644
--- a/app/views/import/github/new.html.haml
+++ b/app/views/import/github/new.html.haml
@@ -38,6 +38,6 @@
       As an administrator you may like to configure
     - else
       Consider asking your GitLab administrator to configure
-    = link_to 'GitHub integration', help_page_path("integration", "github")
+    = link_to 'GitHub integration', help_page_path("integration/github")
     which will allow login via GitHub and allow importing projects without
     generating a Personal Access Token.
diff --git a/app/views/layouts/_collapse_button.html.haml b/app/views/layouts/_collapse_button.html.haml
deleted file mode 100644
index 8c140a5943eb9db8ee56d8dcca8aaea9f96c6fbc..0000000000000000000000000000000000000000
--- a/app/views/layouts/_collapse_button.html.haml
+++ /dev/null
@@ -1,3 +0,0 @@
-= 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/_flash.html.haml b/app/views/layouts/_flash.html.haml
index cc8ea066cb9b98e7b014e625fc0d05fbd803365e..3612f1ce5c693d67df409294db63e50478f2457b 100644
--- a/app/views/layouts/_flash.html.haml
+++ b/app/views/layouts/_flash.html.haml
@@ -1,4 +1,4 @@
-.flash-container
+.flash-container.flash-container-page
   - if alert
     .flash-alert
       = alert
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 8596bbfdef647ed12b1d1e622c2886255584f752..a1a71c2fb3392929b669075ece43dc6174168d51 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -1,6 +1,13 @@
 .page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
   .sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
-    = render partial: 'layouts/collapse_button'
+    .sidebar-action-buttons
+      = link_to '#', class: 'nav-header-btn toggle-nav-collapse', title: "Open/Close" do
+        %span.sr-only Toggle navigation
+        = icon('bars')
+      = link_to '#', class: "nav-header-btn pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: {placement: 'right', container: 'body'} do
+        %span.sr-only Toggle navigation pinning
+        = icon('fw thumb-tack')
+
     - if defined?(sidebar) && sidebar
       = render "layouts/nav/#{sidebar}"
     - elsif current_user
@@ -8,9 +15,6 @@
     - else
       = render 'layouts/nav/explore'
 
-    = link_to '#', class: "nav-header-btn text-center pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: {placement: 'right', container: 'body'} do
-      %span.sr-only Toggle navigation pinning
-      = icon('thumb-tack')
   - if defined?(nav) && nav
     .layout-nav
       .container-fluid
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index 245b9c3b4d447a735cb3074f7a44cb9817787c5f..f7580f00159582c705f2b70fdf04c4ffa832081b 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -44,7 +44,7 @@
           name: "#{j(@project.name)}"
         };
 
-    - if @group and @group.path
+    - if @group && @group.persisted? && @group.path
       :javascript
         gl.groupOptions = gl.groupOptions || {};
         gl.groupOptions["#{j(@group.path)}"] = {
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 11cee421a99ecb35e4d3fdffb699f20cbe1590cd..94c53882623c09f68f7d31327cfd23cbb540e014 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -1,7 +1,7 @@
 %header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class }
   %div{ class: fluid_layout ? "container-fluid" : "container-fluid" }
     .header-content
-      %button.side-nav-toggle{type: 'button'}
+      %button.side-nav-toggle{ type: 'button', "aria-label" => "Toggle global navigation" }
         %span.sr-only Toggle navigation
         = icon('bars')
       %button.navbar-toggle{type: 'button'}
@@ -13,25 +13,25 @@
           %li.hidden-sm.hidden-xs
             = render 'layouts/search' unless current_controller?(:search)
           %li.visible-sm.visible-xs
-            = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+            = link_to search_path, title: 'Search', aria: { label: "Search" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
               = icon('search')
           - if current_user
             - if session[:impersonator_id]
               %li.impersonation
-                = link_to admin_impersonation_path, method: :delete, title: 'Stop Impersonation', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
+                = link_to admin_impersonation_path, method: :delete, title: "Stop Impersonation", aria: { label: 'Stop Impersonation' }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
                   = icon('user-secret fw')
             - if current_user.is_admin?
               %li
-                = link_to admin_root_path, title: 'Admin Area', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+                = link_to admin_root_path, title: 'Admin Area', aria: { label: "Admin Area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
                   = icon('wrench fw')
             %li
-              = link_to dashboard_todos_path, title: 'Todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+              = link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
                 = icon('bell fw')
                 %span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0) }
                   = todos_pending_count
             - if current_user.can_create_project?
               %li
-                = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+                = link_to new_project_path, title: 'New project', aria: { label: "New project" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
                   = icon('plus fw')
             - if Gitlab::Sherlock.enabled?
               %li
@@ -45,12 +45,12 @@
               .dropdown-menu-nav.dropdown-menu-align-right
                 %ul
                   %li
-                    = link_to "Profile", current_user, class: 'profile-link', data: { user: current_user.username }
+                    = link_to "Profile", current_user, class: 'profile-link', aria: { label: "Profile" }, data: { user: current_user.username }
                   %li
-                    = link_to "Profile Settings", profile_path
+                    = link_to "Profile Settings", profile_path, aria: { label: "Profile Settings" }
                   %li.divider
                   %li
-                    = link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link", title: 'Sign out'
+                    = link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link", aria: { label: "Sign out" }
           - else
             %li
               %div
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 52e41b1a8576e9305780f0af1cb922d945db5b2d..216686988143d71713d5707ea92cb59c63d2b741 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -1,64 +1,44 @@
 %ul.nav.nav-sidebar
   = 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-container
-        = icon('bell fw')
       %span
         Todos
         %span.count= number_with_delimiter(todos_pending_count)
   = nav_link(path: 'dashboard#activity') do
     = 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-container
-        = navbar_icon('group')
       %span
         Groups
   = nav_link(controller: 'dashboard/milestones') do
     = link_to dashboard_milestones_path, title: 'Milestones' do
-      .icon-container
-        = navbar_icon('milestones')
       %span
         Milestones
   = nav_link(path: 'dashboard#issues') do
     = 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: '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-container
-        = icon('clipboard fw')
       %span
         Snippets
   = nav_link(controller: :help) do
     = link_to help_path, title: 'Help' do
-      .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-container
-        = icon('user fw')
       %span
         Profile Settings
diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml
index 96fe62c39c3f6b755a56bd15dea8f3d55d0cd158..6d514f669dbacdcef5f6b5eec4c3b43dd83ae0ce 100644
--- a/app/views/layouts/nav/_profile.html.haml
+++ b/app/views/layouts/nav/_profile.html.haml
@@ -18,9 +18,9 @@
           %span
             Applications
     = nav_link(controller: :personal_access_tokens) do
-      = link_to profile_personal_access_tokens_path, title: 'Personal Access Tokens' do
+      = link_to profile_personal_access_tokens_path, title: 'Access Tokens' do
         %span
-          Personal Access Tokens
+          Access Tokens
     = nav_link(controller: :emails) do
       = link_to profile_emails_path, title: 'Emails' do
         %span
diff --git a/app/views/notify/note_merge_request_email.html.haml b/app/views/notify/note_merge_request_email.html.haml
index 35c4b862bb7ed54b838fdad551bc0792f8ee18c2..ea7e3d199fd9c9ebf050aed49bc0365e2fe4a5c6 100644
--- a/app/views/notify/note_merge_request_email.html.haml
+++ b/app/views/notify/note_merge_request_email.html.haml
@@ -1,4 +1,4 @@
-- if @note.diff_note?
+- if @note.diff_note? && @note.diff_file
   %p.details
     New comment on diff for
     = link_to @note.diff_file.file_path, @target_url
diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml
index 6a067a03535023e0efdb8d882fa69ec8aa6d83cc..a42b3b8eb3830cea042ab454a3d0e0c06ad826d7 100644
--- a/app/views/profiles/keys/index.html.haml
+++ b/app/views/profiles/keys/index.html.haml
@@ -11,7 +11,7 @@
       Add an SSH key
     %p.profile-settings-content
       Before you can add an SSH key you need to
-      = link_to "generate it.", help_page_path("ssh", "README")
+      = link_to "generate it.", help_page_path("ssh/README")
     = render 'form'
     %hr
     %h5
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index b4d35dc9a3e4d9ae47c3b1ce6461c6098eb41d11..2afa026847a8a45a54bbc40c35d78f49da6c8ec7 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -43,12 +43,12 @@
     .form-group
       = f.label :dashboard, class: 'label-light' do
         Default Dashboard
-        = link_to('(?)', help_page_path('profile', 'preferences') + '#default-dashboard', target: '_blank')
+        = link_to('(?)', help_page_path('profile/preferences') + '#default-dashboard', target: '_blank')
       = f.select :dashboard, dashboard_choices, {}, class: 'form-control'
     .form-group
       = f.label :project_view, class: 'label-light' do
         Project view
-        = link_to('(?)', help_page_path('profile', 'preferences') + '#default-project-view', target: '_blank')
+        = link_to('(?)', help_page_path('profile/preferences') + '#default-project-view', target: '_blank')
       = f.select :project_view, project_view_choices, {}, class: 'form-control'
       .help-block
         Choose what content you want to see on a project's home page.
diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml
index 5890456bee274f1c7b2873d2d6ad02804f034d13..366f1fed35bb44e15eb1d662d1fa0eaed4cfa39d 100644
--- a/app/views/profiles/two_factor_auths/show.html.haml
+++ b/app/views/profiles/two_factor_auths/show.html.haml
@@ -2,6 +2,10 @@
 - header_title "Two-Factor Authentication", profile_two_factor_auth_path
 = render 'profiles/head'
 
+- if inject_u2f_api?
+  - content_for :page_specific_javascripts do
+    = page_specific_javascript_tag('u2f.js')
+
 .row.prepend-top-default
   .col-lg-3
     %h4.prepend-top-0
@@ -14,7 +18,7 @@
     - 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'))}.
+        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
diff --git a/app/views/projects/_bitbucket_import_modal.html.haml b/app/views/projects/_bitbucket_import_modal.html.haml
index 2987f6b5b226be7e572768187050408192ed8985..e74fd5b93eaae8f289c2c3dd2ddb5f817b8112a5 100644
--- a/app/views/projects/_bitbucket_import_modal.html.haml
+++ b/app/views/projects/_bitbucket_import_modal.html.haml
@@ -10,4 +10,4 @@
           as administrator you need to configure
         - else
           ask your GitLab administrator to configure
-        == #{link_to 'OAuth integration', help_page_path("integration", "bitbucket")}.
+        == #{link_to 'OAuth integration', help_page_path("integration/bitbucket")}.
diff --git a/app/views/projects/_builds_settings.html.haml b/app/views/projects/_builds_settings.html.haml
index 0568c2d305e8f8245775b9cb6a29794e4a13493e..fff30f11d822c85fb758dee89f19a7726e99f80e 100644
--- a/app/views/projects/_builds_settings.html.haml
+++ b/app/views/projects/_builds_settings.html.haml
@@ -4,7 +4,7 @@
   - unless @repository.gitlab_ci_yml
     .form-group
       %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'
+      = link_to 'Get started with Builds', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
   .form-group
     %p Get recent application code using the following command:
     .radio
diff --git a/app/views/projects/_gitlab_import_modal.html.haml b/app/views/projects/_gitlab_import_modal.html.haml
index 377cf0187b880d03126a5e4b8f3e30f3e53776f5..e9f39b16aa7b1e0eb346b47e69285182ebe26068 100644
--- a/app/views/projects/_gitlab_import_modal.html.haml
+++ b/app/views/projects/_gitlab_import_modal.html.haml
@@ -10,4 +10,4 @@
           as administrator you need to configure
         - else
           ask your GitLab administrator to configure
-        == #{link_to 'OAuth integration', help_page_path("integration", "gitlab")}.
+        == #{link_to 'OAuth integration', help_page_path("integration/gitlab")}.
diff --git a/app/views/projects/_merge_request_settings.html.haml b/app/views/projects/_merge_request_settings.html.haml
index 771a2e0df7d7767fa5522ad2386ea24277221f3b..19b4249374b2d65caca94a15925e8258b6e6f33a 100644
--- a/app/views/projects/_merge_request_settings.html.haml
+++ b/app/views/projects/_merge_request_settings.html.haml
@@ -8,4 +8,4 @@
         %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', anchor: 'only-allow-merge-requests-to-be-merged-if-the-build-succeeds')
+        = link_to icon('question-circle'), help_page_path('workflow/merge_requests', anchor: 'only-allow-merge-requests-to-be-merged-if-the-build-succeeds')
diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml
index a131289ee97d9e0767c3fb15a24d795a95928e21..2af625f69cd378e0b66786baf7f1d9d5012459c0 100644
--- a/app/views/projects/builds/index.html.haml
+++ b/app/views/projects/builds/index.html.haml
@@ -11,17 +11,22 @@
           %span.badge.js-totalbuilds-count
             = number_with_delimiter(@all_builds.count(:id))
 
+      %li{class: ('active' if @scope == 'pending')}
+        = link_to project_builds_path(@project, scope: :pending) do
+          Pending
+          %span.badge
+            = number_with_delimiter(@all_builds.pending.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))
+          %span.badge
+            = number_with_delimiter(@all_builds.running.count(:id))
 
       %li{class: ('active' if @scope == 'finished')}
         = link_to project_builds_path(@project, scope: :finished) do
           Finished
-          %span.badge.js-running-count
+          %span.badge
             = number_with_delimiter(@all_builds.finished.count(:id))
 
     .nav-controls
@@ -31,12 +36,12 @@
             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 '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
+  %ul.content-list.builds-content-list
     - if @builds.blank?
       %li
         .nothing-here-block No builds to show
@@ -46,14 +51,10 @@
           %thead
             %tr
               %th Status
-              %th Build ID
               %th Commit
-              %th Ref
               %th Stage
               %th Name
-              %th Tags
-              %th Duration
-              %th Finished at
+              %th
               - if @project.build_coverage_enabled?
                 %th Coverage
               %th
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index 4e801cc72fea0fd8f87b2af1bbfc5d7e8d227f74..4421f3b9562e3cb40e0cc87252f1d8c5e3d390a0 100644
--- a/app/views/projects/builds/show.html.haml
+++ b/app/views/projects/builds/show.html.haml
@@ -67,4 +67,4 @@
 = render "sidebar"
 
 :javascript
-  new CiBuild("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{namespace_project_build_url(@project.namespace, @project, @build, :json)}", "#{@build.status}", "#{trace_with_state[:state]}")
+  new Build("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{namespace_project_build_url(@project.namespace, @project, @build, :json)}", "#{@build.status}", "#{trace_with_state[:state]}")
diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml
index 5bd6e3f0ebc67ad4c8042d3afe183611e9daf5da..e1b42b2cfa5c3ce09063ed137d752e551a0cfa3a 100644
--- a/app/views/projects/ci/builds/_build.html.haml
+++ b/app/views/projects/ci/builds/_build.html.haml
@@ -1,32 +1,45 @@
-%tr.build
+%tr.build.commit
   %td.status
     - if can?(current_user, :read_build, build)
       = ci_status_with_icon(build.status, namespace_project_build_url(build.project.namespace, build.project, build))
     - else
       = ci_status_with_icon(build.status)
 
-  %td.build-link
-    - if can?(current_user, :read_build, build)
-      = link_to namespace_project_build_url(build.project.namespace, build.project, build) do
-        %strong ##{build.id}
-    - else
-      %strong ##{build.id}
+  %td
+    .branch-commit
+      - if can?(current_user, :read_build, build)
+        = link_to namespace_project_build_url(build.project.namespace, build.project, build) do
+          %span ##{build.id}
+      - else
+        %span ##{build.id}
 
-    - if build.stuck?
-      = icon('warning', class: 'text-warning has-tooltip', title: 'Build is stuck. Check runners.')
-    - if defined?(retried) && retried
-      = icon('warning', class: 'text-warning has-tooltip', title: 'Build was retried.')
+      - if build.stuck?
+        = icon('warning', class: 'text-warning has-tooltip', title: 'Build is stuck. Check runners.')
+      - if defined?(retried) && retried
+        = icon('warning', class: 'text-warning has-tooltip', title: 'Build was retried.')
 
-  - if defined?(commit_sha) && commit_sha
-    %td
-      = link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace"
+      - if defined?(ref) && ref
+        - if build.ref
+          = link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref), class: "monospace branch-name"
+        - else
+          .light none
+        = custom_icon("icon_commit")
+
+      - if defined?(commit_sha) && commit_sha
+        = link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "commit-id monospace"
+
+      .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
 
-  - if defined?(ref) && ref
-    %td
-      - if build.ref
-        = link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref)
-      - else
-        .light none
 
   - if defined?(runner) && runner
     %td
@@ -43,25 +56,14 @@
     = build.name
 
   %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
-      #{duration_in_words(build.finished_at, build.started_at)}
-
-  %td.timestamp
+      %p.duration
+        = custom_icon("icon_timer")
+        = duration_in_numbers(build.finished_at, build.started_at)
     - if build.finished_at
-      %span #{time_ago_with_tooltip(build.finished_at)}
+      %p.finished-at
+        = icon("calendar")
+        %span #{time_ago_with_tooltip(build.finished_at)}
 
   - if defined?(coverage) && coverage
     %td.coverage
@@ -79,4 +81,4 @@
             = icon('remove', class: 'cred')
         - elsif defined?(allow_retry) && allow_retry && build.retryable?
           = link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-build' do
-            = icon('refresh')
+            = icon('repeat')
diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml
index af8dd5cd02c5daa79744b909a8f8a5c3840989e4..0557d384e334ddf10bb6c40cfc583aedd6c80232 100644
--- a/app/views/projects/ci/pipelines/_pipeline.html.haml
+++ b/app/views/projects/ci/pipelines/_pipeline.html.haml
@@ -1,17 +1,18 @@
 - 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}
+    = link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline.id) do
+      = ci_status_with_icon(status)
+
 
   %td
-    %div.branch-commit
+    .branch-commit
+      = link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline.id) do
+        %span ##{pipeline.id}
       - if pipeline.ref
-        = link_to pipeline.ref, namespace_project_commits_path(@project.namespace, @project, pipeline.ref), class: "monospace"
-        &middot;
+        = link_to pipeline.ref, namespace_project_commits_path(@project.namespace, @project, pipeline.ref), class: "monospace branch-name"
+        = custom_icon("icon_commit")
       = 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?
@@ -25,12 +26,13 @@
 
       %p.commit-title
         - if commit = pipeline.commit
+          = commit_author_avatar(commit, size: 20)
           = link_to_gfm truncate(commit.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit.id), class: "commit-row-message"
         - else
           Cant find HEAD commit for this branch
 
 
-    - stages_status = pipeline.statuses.stages_status
+    - stages_status = pipeline.statuses.latest.stages_status
     - stages.each do |stage|
       %td
         - status = stages_status[stage]
@@ -45,27 +47,34 @@
   %td
     - if pipeline.started_at && pipeline.finished_at
       %p.duration
+        = custom_icon("icon_timer")
         = duration_in_numbers(pipeline.finished_at, pipeline.started_at)
+    - if pipeline.finished_at
+      %p.finished-at
+        = icon("calendar")
+        #{time_ago_with_tooltip(pipeline.finished_at)}
 
-  %td
+  %td.pipeline-actions
     .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
+        .inline
+          .btn-group
+            %a.dropdown-toggle.btn.btn-default.build-artifacts{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")
+        .cancel-retry-btns.inline
+          - 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/_pipeline.html.haml b/app/views/projects/commit/_pipeline.html.haml
index 0411137b7c61bea020bda1b72508087e662b91e4..41fd545942905323bff2f67a360c58af7d5bb6fb 100644
--- a/app/views/projects/commit/_pipeline.html.haml
+++ b/app/views/projects/commit/_pipeline.html.haml
@@ -42,9 +42,7 @@
         %th Status
         %th Build ID
         %th Name
-        %th Tags
-        %th Duration
-        %th Finished at
+        %th
         - if pipeline.project.build_coverage_enabled?
           %th Coverage
         %th
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 929496f81d887751a7172ad3be0cf1ae342e5452..c8c7b858baa862d6af6125490b66059101edf36c 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -25,7 +25,7 @@
         .commit-actions.hidden-xs
           - if commit.status
             = render_commit_status(commit, cssclass: 'btn btn-transparent')
-          = clipboard_button_with_class({ clipboard_text: commit.id }, css_class: 'btn-transparent')
+          = clipboard_button(clipboard_text: commit.id)
           = 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)
 
diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml
index dd590a4b8ec41ebff64ed9e5679322cc16f3fc4a..af09b3418ea888f1043f17f8c6a127c11f078cf4 100644
--- a/app/views/projects/compare/_form.html.haml
+++ b/app/views/projects/compare/_form.html.haml
@@ -2,15 +2,17 @@
   .clearfix
     - if params[:to] && params[:from]
       = link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has-tooltip', title: 'Switch base of comparison'}
-    .form-group
+    .form-group.dropdown.compare-form-group.js-compare-from-dropdown
       .input-group.inline-input-group
         %span.input-group-addon from
-        = text_field_tag :from, params[:from], class: "form-control", required: true
+        = text_field_tag :from, params[:from], class: "form-control js-compare-dropdown", required: true, data: { refs_url: refs_namespace_project_path(@project.namespace, @project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from].presence }
+      = render "ref_dropdown"
     = "..."
-    .form-group
+    .form-group.dropdown.compare-form-group.js-compare-to-dropdown
       .input-group.inline-input-group
         %span.input-group-addon to
-        = text_field_tag :to, params[:to], class: "form-control", required: true
+        = text_field_tag :to, params[:to], class: "form-control js-compare-dropdown", required: true, data: { refs_url: refs_namespace_project_path(@project.namespace, @project), toggle: "dropdown", target: ".js-compare-to-dropdown", selected: params[:to].presence }
+      = render "ref_dropdown"
     &nbsp;
     = button_tag "Compare", class: "btn btn-create commits-compare-btn"
     - if @merge_request.present?
@@ -19,11 +21,3 @@
       = link_to create_mr_path, class: 'prepend-left-10 btn' do
         = icon("plus")
         Create Merge Request
-
-:javascript
-  var availableTags = #{@project.repository.ref_names.to_json};
-
-  $("#from, #to").autocomplete({
-    source: availableTags,
-    minLength: 1
-  });
diff --git a/app/views/projects/compare/_ref_dropdown.html.haml b/app/views/projects/compare/_ref_dropdown.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..c604c6d0135111125f2f2464118fb4a1caf91c7c
--- /dev/null
+++ b/app/views/projects/compare/_ref_dropdown.html.haml
@@ -0,0 +1,4 @@
+.dropdown-menu.dropdown-menu-selectable
+  = dropdown_title "Select branch/tag"
+  = dropdown_content
+  = dropdown_loading
diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml
index 894c36a96dfbec510539b72e6f52c4280ed99789..901605f7ca3bc05c1f2d757c40ab926f9dafa905 100644
--- a/app/views/projects/deploy_keys/_form.html.haml
+++ b/app/views/projects/deploy_keys/_form.html.haml
@@ -9,5 +9,5 @@
   .form-group
     %p.light.append-bottom-0
       Paste a machine public key here. Read more about how to generate it
-      = link_to "here", help_page_path("ssh", "README")
+      = link_to "here", help_page_path("ssh/README")
   = f.submit "Add key", class: "btn-create btn"
diff --git a/app/views/projects/diffs/_content.html.haml b/app/views/projects/diffs/_content.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..0c0424edffd70c1cc68913b8ec9459006542df38
--- /dev/null
+++ b/app/views/projects/diffs/_content.html.haml
@@ -0,0 +1,29 @@
+.diff-content.diff-wrap-lines
+  - # Skip all non non-supported blobs
+  - return unless blob.respond_to?(:text?)
+  - if diff_file.too_large?
+    .nothing-here-block This diff could not be displayed because it is too large.
+  - elsif blob.only_display_raw?
+    .nothing-here-block This file is too large to display.
+  - elsif blob_text_viewable?(blob)
+    - if !project.repository.diffable?(blob)
+      .nothing-here-block This diff was suppressed by a .gitattributes entry.
+    - elsif diff_file.diff_lines.length > 0
+      - if diff_file.collapsed_by_default? && !expand_all_diffs?
+        - url = url_for(params.merge(action: :diff_for_path, old_path: diff_file.old_path, new_path: diff_file.new_path))
+        .nothing-here-block.diff-collapsed{data: { diff_for_path: url } }
+          This diff is collapsed. Click to expand it.
+      - elsif diff_view == 'parallel'
+        = render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob
+      - else
+        = render "projects/diffs/text_file", diff_file: diff_file
+    - else
+      - if diff_file.mode_changed?
+        .nothing-here-block File mode changed
+      - elsif diff_file.renamed_file
+        .nothing-here-block File moved
+  - elsif blob.image?
+    - old_blob = diff_file.old_blob(diff_commit)
+    = render "projects/diffs/image", diff_file: diff_file, old_file: old_blob, file: blob
+  - else
+    .nothing-here-block No preview for this file type
diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml
index 1975287faee0aac7b928008d40f458832090cbc3..20aaab5accf935e00cd6290f32afb6301120ba98 100644
--- a/app/views/projects/diffs/_diffs.html.haml
+++ b/app/views/projects/diffs/_diffs.html.haml
@@ -6,6 +6,8 @@
 
 .content-block.oneline-block.files-changed
   .inline-parallel-buttons
+    - unless expand_all_diffs?
+      = link_to 'Expand all', url_for(params.merge(expand_all_diffs: 1, format: 'html')), class: 'btn btn-default'
     - if show_whitespace_toggle
       - if current_controller?(:commit)
         = commit_diff_whitespace_link(@project, @commit, class: 'hidden-xs')
@@ -21,7 +23,7 @@
 - if diff_files.overflow?
   = render 'projects/diffs/warning', diff_files: diff_files
 
-.files
+.files{data: {can_create_note: (!@diff_notes_disabled && can?(current_user, :create_note, @project))}}
   - diff_files.each_with_index do |diff_file, index|
     - diff_commit = commit_for_diff(diff_file)
     - blob = diff_file.blob(diff_commit)
diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml
index 3b758a1ec4ecf22011148e50e2c4165045fef9c2..c306909fb1ae6e23b37cbb2e6ddc6c9d243ce9ca 100644
--- a/app/views/projects/diffs/_file.html.haml
+++ b/app/views/projects/diffs/_file.html.haml
@@ -16,28 +16,4 @@
 
         = view_file_btn(diff_commit.id, diff_file, project)
 
-  .diff-content.diff-wrap-lines
-    - # Skip all non non-supported blobs
-    - return unless blob.respond_to?(:text?)
-    - if diff_file.too_large?
-      .nothing-here-block This diff could not be displayed because it is too large.
-    - elsif blob.only_display_raw?
-      .nothing-here-block This file is too large to display.
-    - elsif blob_text_viewable?(blob)
-      - if !project.repository.diffable?(blob)
-        .nothing-here-block This diff was suppressed by a .gitattributes entry.
-      - elsif diff_file.diff_lines.length > 0
-        - if diff_view == 'parallel'
-          = render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i
-        - else
-          = render "projects/diffs/text_file", diff_file: diff_file, index: i
-      - else
-        - if diff_file.mode_changed?
-          .nothing-here-block File mode changed
-        - elsif diff_file.renamed_file
-          .nothing-here-block File moved
-    - elsif blob.image?
-      - old_blob = diff_file.old_blob(diff_commit)
-      = render "projects/diffs/image", diff_file: diff_file, old_file: old_blob, file: blob, index: i
-    - else
-      .nothing-here-block No preview for this file type
+  = render 'projects/diffs/content', diff_file: diff_file, diff_commit: diff_commit, diff_refs: diff_refs, blob: blob, project: project
diff --git a/app/views/projects/diffs/_line.html.haml b/app/views/projects/diffs/_line.html.haml
index 22cad00240ae262b47aec4c99ea84d091e61bb6a..5a8a131d10c162e5252b49b8cdbee31f8a491f54 100644
--- a/app/views/projects/diffs/_line.html.haml
+++ b/app/views/projects/diffs/_line.html.haml
@@ -13,18 +13,15 @@
     %td.line_content.match= line.text
   - else
     %td.old_line.diff-line-num{ class: type, data: { linenumber: line.old_pos } }
-      - link_text = type == "new" ? "&nbsp;".html_safe : line.old_pos
+      - link_text = type == "new" ? " " : line.old_pos
       - if plain
         = link_text
       - else
-        = link_to "", "##{line_code}", id: line_code, data: { linenumber: link_text }
-
-      - if !plain && !@diff_notes_disabled && can?(current_user, :create_note, @project)
-        = link_to_new_diff_note(line_code, position)
+        %a{href: "##{line_code}", data: { linenumber: link_text }}
     %td.new_line.diff-line-num{ class: type, data: { linenumber: line.new_pos } }
-      - link_text = type == "old" ? "&nbsp;".html_safe : line.new_pos
+      - link_text = type == "old" ? " " : line.new_pos
       - if plain
         = link_text
       - else
-        = link_to "", "##{line_code}", id: line_code, data: { linenumber: link_text }
-    %td.line_content.noteable_line{ class: [type, line_code], data: { line_code: line_code, position: position.to_json } }= diff_line_content(line.text, type)
+        %a{href: "##{line_code}", data: { linenumber: link_text }}
+    %td.line_content.noteable_line{ class: type, data: (diff_view_line_data(line_code, position, type) unless plain) }= diff_line_content(line.text, type)
diff --git a/app/views/projects/diffs/_match_line_parallel.html.haml b/app/views/projects/diffs/_match_line_parallel.html.haml
index 0cd888876e02bc4ea7cf0f76fb5006353072af06..b9c0d9dcdfdc33346a241437535d08f70cf29807 100644
--- a/app/views/projects/diffs/_match_line_parallel.html.haml
+++ b/app/views/projects/diffs/_match_line_parallel.html.haml
@@ -1,4 +1,4 @@
-%td.old_line.diff-line-num
+%td.old_line.diff-line-num.empty-cell
 %td.line_content.parallel.match= line
-%td.new_line.diff-line-num
+%td.new_line.diff-line-num.empty-cell
 %td.line_content.parallel.match= line
diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml
index 51f207dce949ac8ffc13536db5fc855cd953d009..d208fcee10b2971f60b338cef6cc4741f5bc338e 100644
--- a/app/views/projects/diffs/_parallel_view.html.haml
+++ b/app/views/projects/diffs/_parallel_view.html.haml
@@ -1,39 +1,34 @@
 / Side-by-side diff view
-%div.text-file.diff-wrap-lines.code.file-content.js-syntax-highlight
+%div.text-file.diff-wrap-lines.code.file-content.js-syntax-highlight{ data: diff_view_data }
   %table
     - diff_file.parallel_diff_lines.each do |line|
       - left = line[:left]
       - right = line[:right]
       %tr.line_holder.parallel
         - if left[:type] == 'match'
-          = render "projects/diffs/match_line_parallel", { line: left[:text],
-          line_old: left[:number], line_new: right[:number] }
+          = render "projects/diffs/match_line_parallel", { line: left[:text] }
         - elsif left[:type] == 'nonewline'
-          %td.old_line.diff-line-num
+          %td.old_line.diff-line-num.empty-cell
           %td.line_content.parallel.match= left[:text]
-          %td.new_line.diff-line-num
+          %td.new_line.diff-line-num.empty-cell
           %td.line_content.parallel.match= left[:text]
         - else
-          %td.old_line.diff-line-num{id: left[:line_code], class: [left[:type], ('empty-cell' unless left[:number])]}
-            = link_to raw(left[:number]), "##{left[:line_code]}", id: left[:line_code]
-            - if !@diff_notes_disabled && can?(current_user, :create_note, @project)
-              = link_to_new_diff_note(left[:line_code], left[:position], 'old')
-          %td.line_content.parallel.noteable_line{class: [left[:type], left[:line_code], ('empty-cell' if left[:text].empty?)], data: { line_code: left[:line_code], position: left[:position].to_json }}= diff_line_content(left[:text])
+          %td.old_line.diff-line-num{id: left[:line_code], class: [left[:type], ('empty-cell' unless left[:number])], data: { linenumber: left[:number] }}
+            %a{href: "##{left[:line_code]}" }= raw(left[:number])
+          %td.line_content.parallel.noteable_line{class: [left[:type], ('empty-cell' if left[:text].empty?)], data: diff_view_line_data(left[:line_code], left[:position], 'old')}= diff_line_content(left[:text])
 
           - if right[:type] == 'new'
-            - new_line_class = 'new'
+            - new_line_type = 'new'
             - new_line_code = right[:line_code]
             - new_position = right[:position]
           - else
-            - new_line_class = nil
+            - new_line_type = nil
             - new_line_code = left[:line_code]
             - new_position = left[:position]
 
-          %td.new_line.diff-line-num{id: new_line_code, class: [new_line_class, ('empty-cell' unless right[:number])], data: { linenumber: right[:number] }}
-            = link_to raw(right[:number]), "##{new_line_code}", id: new_line_code
-            - if !@diff_notes_disabled && can?(current_user, :create_note, @project)
-              = link_to_new_diff_note(new_line_code, new_position, 'new')
-          %td.line_content.parallel.noteable_line{class: [new_line_class, new_line_code, ('empty-cell' if right[:text].empty?)], data: { line_code: new_line_code, position: new_position.to_json }}= diff_line_content(right[:text])
+          %td.new_line.diff-line-num{id: new_line_code, class: [new_line_type, ('empty-cell' unless right[:number])], data: { linenumber: right[:number] }}
+            %a{href: "##{new_line_code}" }= raw(right[:number])
+          %td.line_content.parallel.noteable_line{class: [new_line_type, ('empty-cell' if right[:text].empty?)], data: diff_view_line_data(new_line_code, new_position, 'new')}= diff_line_content(right[:text])
 
       - unless @diff_notes_disabled
         - notes_left, notes_right = organize_comments(left, right)
diff --git a/app/views/projects/diffs/_text_file.html.haml b/app/views/projects/diffs/_text_file.html.haml
index 192093d1273f2ff6bfa42a57e39a8f0f09536cb8..196f8122db367ca0238d3edf23a0eb55f416880b 100644
--- a/app/views/projects/diffs/_text_file.html.haml
+++ b/app/views/projects/diffs/_text_file.html.haml
@@ -3,7 +3,7 @@
   .suppressed-container
     %a.show-suppressed-diff.js-show-suppressed-diff Changes suppressed. Click to show.
 
-%table.text-file.code.js-syntax-highlight{ class: too_big ? 'hide' : '' }
+%table.text-file.code.js-syntax-highlight{ data: diff_view_data, class: too_big ? 'hide' : '' }
   - last_line = 0
   - diff_file.highlighted_diff_lines.each do |line|
     - last_line = line.new_pos
diff --git a/app/views/projects/diffs/_warning.html.haml b/app/views/projects/diffs/_warning.html.haml
index 15536c17f8e86a5091342b39a0caa2bf38005d81..10fa1ddf2e5376578db39b3c530e4b911627e685 100644
--- a/app/views/projects/diffs/_warning.html.haml
+++ b/app/views/projects/diffs/_warning.html.haml
@@ -2,9 +2,6 @@
   %h4
     Too many changes to show.
     .pull-right
-      - unless diff_hard_limit_enabled?
-        = link_to "Reload with full diff", url_for(params.merge(force_show_diff: true, format: nil)), class: "btn btn-sm"
-
       - if current_controller?(:commit) or current_controller?(:merge_requests)
         - if current_controller?(:commit)
           = link_to "Plain diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff), class: "btn btn-sm"
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 27a94fe02dc92d64c4f63fa0a94cfb1b0c64b192..57af167180b254673b29ce1c10d55c3c2b3d0e8b 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -23,7 +23,7 @@
         .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")
+            = 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
diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml
index 5242021243e7d5b5debe12886f0ab38bc510f92f..303d7c23d01b7203af1486e64078880389ba936f 100644
--- a/app/views/projects/environments/index.html.haml
+++ b/app/views/projects/environments/index.html.haml
@@ -17,7 +17,7 @@
         Environments are places where code gets deployed, such as staging or production.
         %br
         = succeed "." do
-          = link_to "Read more about environments", help_page_path("ci", "environments")
+          = link_to "Read more about environments", help_page_path("ci/environments")
       - if can?(current_user, :create_environment, @project)
         = link_to new_namespace_project_environment_path(@project.namespace, @project), class: 'btn btn-create' do
           New environment
diff --git a/app/views/projects/environments/new.html.haml b/app/views/projects/environments/new.html.haml
index da325efecd24a9f15a019549cbdf9185effd9761..89e06567196efa9c39ddc8922d5f265cce708724 100644
--- a/app/views/projects/environments/new.html.haml
+++ b/app/views/projects/environments/new.html.haml
@@ -7,6 +7,6 @@
     %p
       Environments allow you to track deployments of your application
       = succeed "." do
-        = link_to "Read more about environments", help_page_path("ci", "environments")
+        = link_to "Read more about environments", help_page_path("ci/environments")
 
   = render 'form'
diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml
index 53c62ef234d4549ac7cb3582932ebaf79489cca2..b17aba2431fd30db3b4e0f3a2f13aa42844e7ca3 100644
--- a/app/views/projects/environments/show.html.haml
+++ b/app/views/projects/environments/show.html.haml
@@ -20,7 +20,7 @@
         Define environments in the deploy stage(s) in
         %code .gitlab-ci.yml
         to track deployments here.
-      = link_to "Read more", help_page_path("ci", "environments"), class: "btn btn-success"
+      = link_to "Read more", help_page_path("ci/environments"), class: "btn btn-success"
   - else
     .table-holder
       %table.table.environments
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 5bc5c71283ede18a5fa7d540a144b05ec7e58f86..542827b2f1542db5f27be251b453bf5580cd84c2 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
@@ -50,10 +50,12 @@
 
   %td.duration
     - if generic_commit_status.duration
+      = icon("clock-o")
       #{duration_in_words(generic_commit_status.finished_at, generic_commit_status.started_at)}
 
   %td.timestamp
     - if generic_commit_status.finished_at
+      = icon("calendar")
       %span #{time_ago_with_tooltip(generic_commit_status.finished_at)}
 
   - if defined?(coverage) && coverage
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index 312bd86ed04e7747d72e326c30abf92c28114519..7612fe3719aa2c4f0be95d3f2f0c97cabf849303 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -32,7 +32,7 @@
         Code, test, and deploy together
     .blank-state
       .blank-state-icon
-        = navbar_icon("issues", size: 50)
+        = custom_icon("issues", size: 50)
       %h3.blank-state-title
         You don't have any issues right now.
       %p.blank-state-text
diff --git a/app/views/projects/merge_requests/show/_how_to_merge.html.haml b/app/views/projects/merge_requests/show/_how_to_merge.html.haml
index b3bea900d4244d0ed7769f6f679da0f296ad8304..b727efaa6a670f4d77abb535ed6e2904903e3dfb 100644
--- a/app/views/projects/merge_requests/show/_how_to_merge.html.haml
+++ b/app/views/projects/merge_requests/show/_how_to_merge.html.haml
@@ -8,7 +8,7 @@
         %p
           %strong Step 1.
           Fetch and check out the branch for this merge request
-        = clipboard_button_with_class({clipboard_target: "pre#merge-info-1"}, css_class: "btn-clipboard")
+        = clipboard_button(clipboard_target: "pre#merge-info-1")
         %pre.dark#merge-info-1
           - if @merge_request.for_fork?
             :preserve
@@ -25,7 +25,7 @@
         %p
           %strong Step 3.
           Merge the branch and fix any conflicts that come up
-        = clipboard_button_with_class({clipboard_target: "pre#merge-info-3"}, css_class: "btn-clipboard")
+        = clipboard_button(clipboard_target: "pre#merge-info-3")
         %pre.dark#merge-info-3
           - if @merge_request.for_fork?
             :preserve
@@ -38,7 +38,7 @@
         %p
           %strong Step 4.
           Push the result of the merge to GitLab
-        = clipboard_button_with_class({clipboard_target: "pre#merge-info-4"}, css_class: "btn-clipboard")
+        = clipboard_button(clipboard_target: "pre#merge-info-4")
         %pre.dark#merge-info-4
           :preserve
             git push origin #{h @merge_request.target_branch}
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 5bf5210aeab390a644157536af4a538b59dd0e50..b24bdf22ceb8783a307fd76796d75204ba458740 100644
--- a/app/views/projects/merge_requests/show/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_title.html.haml
@@ -19,13 +19,13 @@
           Options
         .dropdown-menu.dropdown-menu-align-right.hidden-lg
           %ul
-            %li{ class: issue_button_visibility(@merge_request, true) }
+            %li{ class: merge_request_button_visibility(@merge_request, true) }
               = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, title: 'Close merge request'
-            %li{ class: issue_button_visibility(@merge_request, false) }
+            %li{ class: merge_request_button_visibility(@merge_request, false) }
               = 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-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 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: "hidden-xs hidden-sm btn btn-grouped btn-close #{merge_request_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 #{merge_request_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/new.html.haml b/app/views/projects/new.html.haml
index 05f33b78a47e4e0394b1a17a91c4543acc0c1055..c72d0140bb9c9faf15a6aa1f7b052d985279b1f0 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -107,7 +107,7 @@
         .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")
+            = link_to "(?)", help_page_path("public_access/public_access")
           = render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: @project.visibility_level, form_model: @project)
 
         = f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
@@ -154,4 +154,9 @@
       $('.import_gitlab_project').attr('disabled',true);
       $('.import_gitlab_project').attr('title', 'Project path required.');
     }
-  })
+  });
+
+  $('.import_git').click(function( event ) {
+    $projectImportUrl = $('#project_import_url')
+    $projectImportUrl.attr('disabled', !$projectImportUrl.attr('disabled'))
+  });
\ No newline at end of file
diff --git a/app/views/projects/notes/_hints.html.haml b/app/views/projects/notes/_hints.html.haml
index 7d1cbc62e86dc83ecfee4471cd42529363fa4f4f..25466e7562e04e8bf19d92e1190f6c3fc516e12b 100644
--- a/app/views/projects/notes/_hints.html.haml
+++ b/app/views/projects/notes/_hints.html.haml
@@ -1,7 +1,7 @@
 .comment-toolbar.clearfix
   .toolbar-text
     Styling with
-    = link_to 'Markdown', help_page_path('markdown', 'markdown'), target: '_blank', tabindex: -1
+    = link_to 'Markdown', help_page_path('markdown/markdown'), target: '_blank', tabindex: -1
     is supported
   %button.toolbar-button.markdown-selector{ type: 'button', tabindex: '-1' }
     = icon('file-image-o', class: 'toolbar-button-icon')
diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml
index 1c39ce897a3fb148f06187a5d9f21465f2f3704e..56d302fab82540fd072f73f3106329d43d13f61c 100644
--- a/app/views/projects/notes/_notes_with_form.html.haml
+++ b/app/views/projects/notes/_notes_with_form.html.haml
@@ -2,6 +2,8 @@
   = render "projects/notes/notes"
 %ul.notes.notes-form.timeline
   %li.timeline-entry
+    .flash-container.timeline-content
+
     - if can? current_user, :create_note, @project
       .timeline-icon.hidden-xs.hidden-sm
         %a.author_link{ href: user_path(current_user) }
diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml
index 6a127afa410f1fb4138d061a53b8c07f1ef50dde..5f466bdbac2c89882907ffcce380028984590142 100644
--- a/app/views/projects/pipelines/index.html.haml
+++ b/app/views/projects/pipelines/index.html.haml
@@ -28,10 +28,10 @@
     .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
+          Run 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 '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
@@ -45,13 +45,13 @@
       .table-holder
         %table.table.builds
           %tbody
-            %th ID
+            %th Status
             %th Commit
             - stages.each do |stage|
               %th.stage
                 %span.has-tooltip{ title: "#{stage.titleize}" }
                   = stage.titleize
-            %th Duration
+            %th
             %th
           = render @pipelines, commit_sha: true, stage: true, allow_retry: true, stages: stages
 
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 82892a3335828e877ba7510598c6fe34bca68e4d..978c4dfc5ec92af04008bf2241d51b32875d4a9b 100644
--- a/app/views/projects/project_members/_new_project_member.html.haml
+++ b/app/views/projects/project_members/_new_project_member.html.haml
@@ -12,7 +12,7 @@
       = 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"
+        %strong= link_to "here", help_page_path("user/permissions"), class: "vlink"
 
   .form-actions
     = f.submit 'Add users to project', class: "btn btn-create"
diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml
index 5669713d9a15038f12fa078910b0021ac269dcec..883d3e3af1eabc9e94e82112c4b7a80087bb3d29 100644
--- a/app/views/projects/protected_branches/index.html.haml
+++ b/app/views/projects/protected_branches/index.html.haml
@@ -8,10 +8,10 @@
     %p.prepend-top-20
       Protected branches are designed to:
       %ul
-        %li prevent pushes from everybody except #{link_to "masters", help_page_path("permissions", "permissions"), class: "vlink"}
+        %li prevent pushes from everybody except #{link_to "masters", help_page_path("user/permissions"), class: "vlink"}
         %li prevent anyone from force pushing to the branch
         %li prevent anyone from deleting the branch
-      %p.append-bottom-0 Read more about #{link_to "project permissions", help_page_path("permissions", "permissions"), class: "underlined-link"}
+      %p.append-bottom-0 Read more about #{link_to "project permissions", help_page_path("user/permissions"), class: "underlined-link"}
   .col-lg-9
     %h5.prepend-top-0
       Protect a branch
@@ -23,7 +23,7 @@
           = f.label :name, "Branch", class: "label-light"
           = render partial: "dropdown", locals: { f: f }
           %p.help-block
-            = link_to "Wildcards", help_page_path(category: 'workflow', file: 'protected_branches', format: 'md', anchor: "wildcard-protected-branches")
+            = link_to "Wildcards", help_page_path('workflow/protected_branches', anchor: "wildcard-protected-branches")
             such as
             %code *-stable
             or
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 58d8e068754559b15ec84e4cacef09ff898635a1..dd1cf680cfadbc129e32ba20b05ed1edc153a50f 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -82,4 +82,4 @@
         Archived project! Repository is read-only
 
   %div{class: "project-show-#{default_project_view}"}
-    = render default_project_view
+    = render default_project_view
\ No newline at end of file
diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml
index 6c994ae486b98c33167b27d3fcc64f1b364e0a5f..1646bcf4b8ab023bd9e6cf694a61caecace0bf10 100644
--- a/app/views/projects/snippets/index.html.haml
+++ b/app/views/projects/snippets/index.html.haml
@@ -1,6 +1,6 @@
 - page_title "Snippets"
 
-.row-content-block.top-block
+.sub-header-block
   .pull-right
     - if can?(current_user, :create_project_snippet, @project)
       = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do
diff --git a/app/views/shared/_import_form.html.haml b/app/views/shared/_import_form.html.haml
index 627814bcfae11e1bf33e59bd0de20090a3f1e1d5..65a3a6bddab3fdd9bbb50a54631eb37a996a0f17 100644
--- a/app/views/shared/_import_form.html.haml
+++ b/app/views/shared/_import_form.html.haml
@@ -2,7 +2,7 @@
   = f.label :import_url, class: 'control-label' do
     %span Git repository URL
   .col-sm-10
-    = f.text_field :import_url, class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git'
+    = f.text_field :import_url, class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git', disabled: true
 
     .well.prepend-top-20
       %ul
diff --git a/app/views/shared/_visibility_level.html.haml b/app/views/shared/_visibility_level.html.haml
index 1c6ec198d3d460425f73702aa7cbb92eeb303623..107ad19177c9e1a314923ef3109db4dc12cf403a 100644
--- a/app/views/shared/_visibility_level.html.haml
+++ b/app/views/shared/_visibility_level.html.haml
@@ -1,7 +1,7 @@
 .form-group.project-visibility-level-holder
   = f.label :visibility_level, class: 'control-label' do
     Visibility Level
-    = link_to "(?)", help_page_path("public_access", "public_access")
+    = link_to "(?)", help_page_path("public_access/public_access")
   .col-sm-10
     - if can_change_visibility_level
       = render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: visibility_level, form_model: form_model)
diff --git a/app/views/shared/icons/_group.svg b/app/views/shared/icons/_group.svg.erb
similarity index 83%
rename from app/views/shared/icons/_group.svg
rename to app/views/shared/icons/_group.svg.erb
index 75cae0d16c86a9de6d82e9270ba73545e6651a85..53635016900d477497aaf2ce71144e4844b3057c 100644
--- a/app/views/shared/icons/_group.svg
+++ b/app/views/shared/icons/_group.svg.erb
@@ -1,9 +1,4 @@
-<?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>
+<svg width="<%= size %>" height="<%= size %>" viewBox="0 0 16 16">
     <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>
@@ -15,4 +10,4 @@
             <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
+</svg>
diff --git a/app/views/shared/icons/_icon_commit.svg b/app/views/shared/icons/_icon_commit.svg
new file mode 100644
index 0000000000000000000000000000000000000000..0e96035b7b74ec9232d18f33ba094f4f2bdb9bb7
--- /dev/null
+++ b/app/views/shared/icons/_icon_commit.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
+  <path fill="#8F8F8F" fill-rule="evenodd" d="M28.7769836,18 C27.8675252,13.9920226 24.2831748,11 20,11 C15.7168252,11 12.1324748,13.9920226 11.2230164,18 L4.0085302,18 C2.90195036,18 2,18.8954305 2,20 C2,21.1122704 2.8992496,22 4.0085302,22 L11.2230164,22 C12.1324748,26.0079774 15.7168252,29 20,29 C24.2831748,29 27.8675252,26.0079774 28.7769836,22 L35.9914698,22 C37.0980496,22 38,21.1045695 38,20 C38,18.8877296 37.1007504,18 35.9914698,18 L28.7769836,18 L28.7769836,18 Z M20,25 C22.7614237,25 25,22.7614237 25,20 C25,17.2385763 22.7614237,15 20,15 C17.2385763,15 15,17.2385763 15,20 C15,22.7614237 17.2385763,25 20,25 L20,25 Z"/>
+</svg>
diff --git a/app/views/shared/icons/_icon_timer.svg b/app/views/shared/icons/_icon_timer.svg
new file mode 100644
index 0000000000000000000000000000000000000000..0b1e5804427f1d0f1a3ed05cbae46cd23f88fa20
--- /dev/null
+++ b/app/views/shared/icons/_icon_timer.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><g fill="#8F8F8F" fill-rule="evenodd"><path d="M29.513 10.134A15.922 15.922 0 0 0 23 7.28V6h2.993C26.55 6 27 5.552 27 5V2a1 1 0 0 0-1.007-1H14.007C13.45 1 13 1.448 13 2v3a1 1 0 0 0 1.007 1H17v1.28C9.597 8.686 4 15.19 4 23c0 8.837 7.163 16 16 16s16-7.163 16-16c0-3.461-1.099-6.665-2.967-9.283l1.327-1.58a2.498 2.498 0 0 0-.303-3.53 2.499 2.499 0 0 0-3.528.315l-1.016 1.212zM20 34c6.075 0 11-4.925 11-11s-4.925-11-11-11S9 16.925 9 23s4.925 11 11 11z"/><path d="M19 21h-4.002c-.552 0-.998.452-.998 1.01v1.98c0 .567.447 1.01.998 1.01h7.004c.274 0 .521-.111.701-.291a.979.979 0 0 0 .297-.704v-8.01c0-.54-.452-.995-1.01-.995h-1.98a.997.997 0 0 0-1.01.995V21z"/></g></svg>
\ No newline at end of file
diff --git a/app/views/shared/icons/_project.svg b/app/views/shared/icons/_project.svg
deleted file mode 100644
index 1e8b43f8c6b0d3754f4eb9b4e925533f11b50740..0000000000000000000000000000000000000000
--- a/app/views/shared/icons/_project.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-<?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/_project.svg.erb b/app/views/shared/icons/_project.svg.erb
new file mode 100644
index 0000000000000000000000000000000000000000..2f60bb7245ee728aba339a39534f1376edcb6460
--- /dev/null
+++ b/app/views/shared/icons/_project.svg.erb
@@ -0,0 +1,3 @@
+<svg width="<%= size %>" height="<%= size %>" viewBox="0 0 16 16">
+  <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" fill-rule="evenodd"></path>
+</svg>
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index adfab1af53eb3b316952e2067c0330d9faf98b44..e020a7d4d00ccedca7669c011b2008b67041a8ac 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -19,7 +19,7 @@
 
     = 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
-        .sidebar-collapsed-icon.sidebar-collapsed-user{data: {toggle: "tooltip", placement: "left", container: "body"}, title: (issuable.assignee.to_reference if issuable.assignee)}
+        .sidebar-collapsed-icon.sidebar-collapsed-user{data: {toggle: "tooltip", placement: "left", container: "body"}, title: (issuable.assignee.name if issuable.assignee)}
           - if issuable.assignee
             = link_to_member(@project, issuable.assignee, size: 24)
           - else
diff --git a/app/views/shared/milestones/_summary.html.haml b/app/views/shared/milestones/_summary.html.haml
index 975c74f4ea6548ffa02b27345730db2979a88dd9..dee2472fa79f07f47ed5f8ca967598d08ae97535 100644
--- a/app/views/shared/milestones/_summary.html.haml
+++ b/app/views/shared/milestones/_summary.html.haml
@@ -26,7 +26,6 @@
     %span.pull-right.tab-issues-buttons
       - if project && can?(current_user, :create_issue, project)
         = link_to new_namespace_project_issue_path(project.namespace, project, issue: { milestone_id: milestone.id }), class: "btn  btn-grouped", title: "New Issue" do
-          %i.fa.fa-plus
           New Issue
       = link_to 'Browse Issues', milestones_browse_issuables_path(milestone, type: :issues), class: "btn btn-grouped"
     %span.pull-right.tab-merge-requests-buttons.hidden
diff --git a/app/views/shared/web_hooks/_form.html.haml b/app/views/shared/web_hooks/_form.html.haml
index d1e861ca80c8640e27c5df5c7009dd22ea1debcb..2585ed9360bf5f5ec55dbeb902a495b9dc0edb6c 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")} can be
+      #{link_to "Webhooks", help_page_path("web_hooks/web_hooks")} can be
       used for binding events when something is happening within the project.
   .col-lg-9.append-bottom-default
     = form_for hook, as: :hook, url: polymorphic_path(url_components + [:hooks]) do |f|
diff --git a/app/workers/git_garbage_collect_worker.rb b/app/workers/git_garbage_collect_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a6cefd4d60175afa3231dd340090627766df2567
--- /dev/null
+++ b/app/workers/git_garbage_collect_worker.rb
@@ -0,0 +1,16 @@
+class GitGarbageCollectWorker
+  include Sidekiq::Worker
+  include Gitlab::ShellAdapter
+
+  sidekiq_options queue: :gitlab_shell, retry: false
+
+  def perform(project_id)
+    project = Project.find(project_id)
+
+    gitlab_shell.gc(project.repository_storage_path, project.path_with_namespace)
+    # Refresh the branch cache in case garbage collection caused a ref lookup to fail
+    project.repository.after_create_branch
+    project.repository.branch_names
+    project.repository.has_visible_content?
+  end
+end
diff --git a/app/workers/gitlab_shell_one_shot_worker.rb b/app/workers/gitlab_shell_one_shot_worker.rb
deleted file mode 100644
index 4ddbcf574d5eba6057fd53229709387a9220affc..0000000000000000000000000000000000000000
--- a/app/workers/gitlab_shell_one_shot_worker.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-class GitlabShellOneShotWorker
-  include Sidekiq::Worker
-  include Gitlab::ShellAdapter
-
-  sidekiq_options queue: :gitlab_shell, retry: false
-
-  def perform(action, *arg)
-    gitlab_shell.send(action, *arg)
-  end
-end
diff --git a/app/workers/project_export_worker.rb b/app/workers/project_export_worker.rb
index 39f6037e0777ea2cd4c6eb49d2c015a53c97c61f..615311e63f50efaef365bbf8e028b49a669b34ab 100644
--- a/app/workers/project_export_worker.rb
+++ b/app/workers/project_export_worker.rb
@@ -1,7 +1,7 @@
 class ProjectExportWorker
   include Sidekiq::Worker
 
-  sidekiq_options queue: :gitlab_shell, retry: true
+  sidekiq_options queue: :gitlab_shell, retry: 3
 
   def perform(current_user_id, project_id)
     current_user = User.find(current_user_id)
diff --git a/config/application.rb b/config/application.rb
index 21e7cc7b6e8f1c7fe664f1118b155125c006d582..5f7b6a3c049467e62001965f6639782dcc59daa7 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -87,6 +87,7 @@ module Gitlab
     config.assets.precompile << "profile/application.js"
     config.assets.precompile << "lib/utils/*.js"
     config.assets.precompile << "lib/*.js"
+    config.assets.precompile << "u2f.js"
 
     # Version of your assets, change this if you want to expire all your assets
     config.assets.version = '1.0'
diff --git a/config/gitlab.teatro.yml b/config/gitlab.teatro.yml
deleted file mode 100644
index 75b79b837e0c75a3dd9c1fa3bd9b10fae294fa86..0000000000000000000000000000000000000000
--- a/config/gitlab.teatro.yml
+++ /dev/null
@@ -1,87 +0,0 @@
-
-production: &base
-  gitlab:
-    host: localhost
-    port: 80
-    https: false
-
-    user: root
-
-    email_from: example@example.com
-
-    support_email: support@example.com
-
-    default_projects_features:
-      issues: true
-      merge_requests: true
-      wiki: true
-      snippets: false
-      visibility_level: "private"  # can be "private" | "internal" | "public"
-
-  issues_tracker:
-
-  gravatar:
-    enabled: true                 # Use user avatar image from Gravatar.com (default: true)
-
-  ldap:
-    enabled: false
-    host: '_your_ldap_server'
-    port: 636
-    uid: 'sAMAccountName'
-    method: 'ssl' # "tls" or "ssl" or "plain"
-    bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
-    password: '_the_password_of_the_bind_user'
-    allow_username_or_email_login: true
-
-    base: ''
-
-    user_filter: ''
-
-  omniauth:
-    enabled: false
-
-  satellites:
-    # Relative paths are relative to Rails.root (default: tmp/repo_satellites/)
-    path: /apps/gitlab-satellites/
-
-  backup:
-    path: "tmp/backups"   # Relative paths are relative to Rails.root (default: tmp/backups/)
-
-  repositories:
-    storages: # REPO PATHS MUST NOT BE A SYMLINK!!!
-      default: /apps/repositories/
-
-  gitlab_shell:
-    path: /apps/gitlab-shell/
-
-    hooks_path: /apps/gitlab-shell/hooks/
-
-    upload_pack: true
-    receive_pack: true
-
-  git:
-    bin_path: /usr/bin/git
-    max_size: 5242880 # 5.megabytes
-    timeout: 10
-
-  extra:
-
-development:
-  <<: *base
-
-test:
-  <<: *base
-  gravatar:
-    enabled: true
-  gitlab:
-    host: localhost
-    port: 80
-  issues_tracker:
-    redmine:
-      title: "Redmine"
-      project_url: "http://redmine/projects/:issues_tracker_id"
-      issues_url: "http://redmine/:project_id/:issues_tracker_id/:id"
-      new_issue_url: "http://redmine/projects/:issues_tracker_id/issues/new"
-
-staging:
-  <<: *base
diff --git a/config/initializers/health_check.rb b/config/initializers/health_check.rb
index 6796407d4e64b5427112c36e980674c69c09366f..4c91a61fb4a77ef627873749d912d516410dc947 100644
--- a/config/initializers/health_check.rb
+++ b/config/initializers/health_check.rb
@@ -1,16 +1,3 @@
-# Email forcibly included in the standard checks, but the email health check
-# doesn't support the full range of SMTP options, which can result in failures
-# for valid SMTP configurations.
-# Overwrite the HealthCheck's detection of whether email is configured
-# in order to avoid the email check during standard checks
-module HealthCheck
-  class Utils
-    def self.mailer_configured?
-      false
-    end
-  end
-end
-
 HealthCheck.setup do |config|
   config.standard_checks = ['database', 'migrations', 'cache']
   config.full_checks = ['database', 'migrations', 'cache']
diff --git a/config/routes.rb b/config/routes.rb
index ea6465038df1c4b9104bb1e3d4a5f5135979c1a0..97c1e8d2154a694cf49a0f70e191b12d19bfe625 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -89,8 +89,9 @@ Rails.application.routes.draw do
   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'
-  get 'help/:category/:file'  => 'help#show', as: :help_page, constraints: { category: /.*/, file: /[^\/\.]+/ }
+  get 'help/*path'            => 'help#show', as: :help_page
   get 'help/shortcuts'
   get 'help/ui' => 'help#ui'
 
@@ -615,10 +616,18 @@ Rails.application.routes.draw do
             post :retry_builds
             post :revert
             post :cherry_pick
+            get :diff_for_path
           end
         end
 
-        resources :compare, only: [:index, :create]
+        resources :compare, only: [:index, :create] do
+          collection do
+            get :diff_for_path
+          end
+        end
+
+        get '/compare/:from...:to', to: 'compare#show', as: 'compare', constraints: { from: /.+/, to: /.+/ }
+
         resources :network, only: [:show], constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ }
 
         resources :graphs, only: [:show], constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ } do
@@ -629,9 +638,6 @@ Rails.application.routes.draw do
           end
         end
 
-        get '/compare/:from...:to' => 'compare#show', :as => 'compare',
-            :constraints => { from: /.+/, to: /.+/ }
-
         resources :snippets, constraints: { id: /\d+/ } do
           member do
             get 'raw'
@@ -706,12 +712,14 @@ Rails.application.routes.draw do
             post :toggle_subscription
             post :toggle_award_emoji
             post :remove_wip
+            get :diff_for_path
           end
 
           collection do
             get :branch_from
             get :branch_to
             get :update_branches
+            get :diff_for_path
           end
         end
 
diff --git a/db/migrate/20160712171823_remove_award_emojis_with_no_user.rb b/db/migrate/20160712171823_remove_award_emojis_with_no_user.rb
new file mode 100644
index 0000000000000000000000000000000000000000..668c22bb51c7a33192b0f640aedd778aaad43be2
--- /dev/null
+++ b/db/migrate/20160712171823_remove_award_emojis_with_no_user.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 RemoveAwardEmojisWithNoUser < 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
+    AwardEmoji.joins('LEFT JOIN users ON users.id = user_id').where('users.id IS NULL').destroy_all
+  end
+end
diff --git a/db/migrate/20160715132507_add_user_id_to_pipeline.rb b/db/migrate/20160715132507_add_user_id_to_pipeline.rb
new file mode 100644
index 0000000000000000000000000000000000000000..af0461c4daf9e64749a35e92a1d05943c1c6b5ec
--- /dev/null
+++ b/db/migrate/20160715132507_add_user_id_to_pipeline.rb
@@ -0,0 +1,7 @@
+class AddUserIdToPipeline < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  def change
+    add_column :ci_commits, :user_id, :integer
+  end
+end
diff --git a/db/migrate/20160715134306_add_index_for_pipeline_user_id.rb b/db/migrate/20160715134306_add_index_for_pipeline_user_id.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7c991c6d998d29f5f4124eeda64ac67fb1da9911
--- /dev/null
+++ b/db/migrate/20160715134306_add_index_for_pipeline_user_id.rb
@@ -0,0 +1,9 @@
+class AddIndexForPipelineUserId < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  disable_ddl_transaction!
+
+  def change
+    add_concurrent_index :ci_commits, :user_id
+  end
+end
diff --git a/db/migrate/20160716115710_add_when_and_yaml_variables_to_ci_builds.rb b/db/migrate/20160716115710_add_when_and_yaml_variables_to_ci_builds.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3e084023a650392fd4e0e7ebb23484483d604163
--- /dev/null
+++ b/db/migrate/20160716115710_add_when_and_yaml_variables_to_ci_builds.rb
@@ -0,0 +1,8 @@
+class AddWhenAndYamlVariablesToCiBuilds < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  def change
+    add_column :ci_builds, :when, :string
+    add_column :ci_builds, :yaml_variables, :text
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index a5eea3a697cba601c92b28fe5578570b3b90f162..d250d65fe21f23f7e379866dda847ac142b4a376 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: 20160705163108) do
+ActiveRecord::Schema.define(version: 20160716115710) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -168,6 +168,8 @@ ActiveRecord::Schema.define(version: 20160705163108) do
     t.string   "environment"
     t.datetime "artifacts_expire_at"
     t.integer  "artifacts_size"
+    t.string   "when"
+    t.text     "yaml_variables"
   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
@@ -199,6 +201,7 @@ ActiveRecord::Schema.define(version: 20160705163108) do
     t.datetime "started_at"
     t.datetime "finished_at"
     t.integer  "duration"
+    t.integer  "user_id"
   end
 
   add_index "ci_commits", ["gl_project_id", "sha"], name: "index_ci_commits_on_gl_project_id_and_sha", using: :btree
@@ -210,6 +213,7 @@ ActiveRecord::Schema.define(version: 20160705163108) do
   add_index "ci_commits", ["project_id"], name: "index_ci_commits_on_project_id", using: :btree
   add_index "ci_commits", ["sha"], name: "index_ci_commits_on_sha", using: :btree
   add_index "ci_commits", ["status"], name: "index_ci_commits_on_status", using: :btree
+  add_index "ci_commits", ["user_id"], name: "index_ci_commits_on_user_id", using: :btree
 
   create_table "ci_events", force: :cascade do |t|
     t.integer  "project_id"
diff --git a/doc/README.md b/doc/README.md
index cf7a828d91e0b30f9edb90e0845cfff14560572a..cc0b6e0c1e52d66fef8f7b53e604825e5eff7185 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -11,7 +11,7 @@
 - [Importing and exporting projects between instances](user/project/settings/import_export.md).
 - [Markdown](markdown/markdown.md) GitLab's advanced formatting system.
 - [Migrating from SVN](workflow/importing/migrating_from_svn.md) Convert a SVN repository to Git and GitLab.
-- [Permissions](permissions/permissions.md) Learn what each role in a project (external/guest/reporter/developer/master/owner) can do.
+- [Permissions](user/permissions.md) Learn what each role in a project (external/guest/reporter/developer/master/owner) can do.
 - [Profile Settings](profile/README.md)
 - [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.
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 3ced787b23e7cadf3b240a69b7787f15a0c45a83..419fb8f85d8ace0393e8f123e7c1ce77735c34d5 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -78,7 +78,8 @@ Example response:
       "iid" : 6,
       "labels" : [],
       "subscribed" : false,
-      "user_notes_count": 1
+      "user_notes_count": 1,
+      "due_date": "2016-07-22"
    }
 ]
 ```
@@ -154,7 +155,8 @@ Example response:
       "updated_at" : "2016-01-04T15:31:46.176Z",
       "created_at" : "2016-01-04T15:31:46.176Z",
       "subscribed" : false,
-      "user_notes_count": 1
+      "user_notes_count": 1,
+      "due_date": null
    }
 ]
 ```
@@ -232,7 +234,8 @@ Example response:
       "updated_at" : "2016-01-04T15:31:46.176Z",
       "created_at" : "2016-01-04T15:31:46.176Z",
       "subscribed" : false,
-      "user_notes_count": 1
+      "user_notes_count": 1,
+      "due_date": "2016-07-22"
    }
 ]
 ```
@@ -295,7 +298,8 @@ Example response:
    "updated_at" : "2016-01-04T15:31:46.176Z",
    "created_at" : "2016-01-04T15:31:46.176Z",
    "subscribed": false,
-   "user_notes_count": 1
+   "user_notes_count": 1,
+   "due_date": null
 }
 ```
 
@@ -320,6 +324,7 @@ POST /projects/:id/issues
 | `milestone_id`  | integer | no  | The ID of a milestone to assign issue |
 | `labels`        | string  | no  | Comma-separated label names for an issue  |
 | `created_at`    | string  | no  | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` |
+| `due_date`      | string  | no   | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` |
 
 ```bash
 curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/issues?title=Issues%20with%20auth&labels=bug
@@ -351,7 +356,8 @@ Example response:
    "updated_at" : "2016-01-07T12:44:33.959Z",
    "milestone" : null,
    "subscribed" : true,
-   "user_notes_count": 0
+   "user_notes_count": 0,
+   "due_date": null
 }
 ```
 
@@ -379,6 +385,7 @@ PUT /projects/:id/issues/:issue_id
 | `labels`        | string  | no  | Comma-separated label names for an issue  |
 | `state_event`   | string  | no  | The state event of an issue. Set `close` to close the issue and `reopen` to reopen it |
 | `updated_at`    | string  | no  | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` |
+| `due_date`      | string  | no   | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` |
 
 ```bash
 curl -X PUT -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/issues/85?state_event=close
@@ -410,7 +417,8 @@ Example response:
    "assignee" : null,
    "milestone" : null,
    "subscribed" : true,
-   "user_notes_count": 0
+   "user_notes_count": 0,
+   "due_date": "2016-07-22"
 }
 ```
 
@@ -487,7 +495,8 @@ Example response:
     "state": "active",
     "avatar_url": "http://www.gravatar.com/avatar/7a190fecbaa68212a4b68aeb6e3acd10?s=80&d=identicon",
     "web_url": "https://gitlab.example.com/u/solon.cremin"
-  }
+  },
+  "due_date": null
 }
 ```
 
@@ -541,7 +550,8 @@ Example response:
     "state": "active",
     "avatar_url": "http://www.gravatar.com/avatar/7a190fecbaa68212a4b68aeb6e3acd10?s=80&d=identicon",
     "web_url": "https://gitlab.example.com/u/solon.cremin"
-  }
+  },
+  "due_date": null
 }
 ```
 
@@ -596,7 +606,8 @@ Example response:
     "avatar_url": "http://www.gravatar.com/avatar/5224fd70153710e92fb8bcf79ac29d67?s=80&d=identicon",
     "web_url": "https://gitlab.example.com/u/orville"
   },
-  "subscribed": false
+  "subscribed": false,
+  "due_date": null
 }
 ```
 
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 816f09e1007c281953f20ad331fa8017ba360b98..a8c3b068d22f1ec43f3c3e1fd20892191a080721 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -68,7 +68,9 @@ Parameters:
     "merge_when_build_succeeds": true,
     "merge_status": "can_be_merged",
     "subscribed" : false,
-    "user_notes_count": 1
+    "user_notes_count": 1,
+    "should_remove_source_branch": true,
+    "force_remove_source_branch": false
   }
 ]
 ```
@@ -132,7 +134,9 @@ Parameters:
   "merge_when_build_succeeds": true,
   "merge_status": "can_be_merged",
   "subscribed" : true,
-  "user_notes_count": 1
+  "user_notes_count": 1,
+  "should_remove_source_branch": true,
+  "force_remove_source_branch": false
 }
 ```
 
@@ -233,6 +237,8 @@ Parameters:
   "merge_status": "can_be_merged",
   "subscribed" : true,
   "user_notes_count": 1,
+  "should_remove_source_branch": true,
+  "force_remove_source_branch": false,
   "changes": [
     {
     "old_path": "VERSION",
@@ -312,7 +318,9 @@ Parameters:
   "merge_when_build_succeeds": true,
   "merge_status": "can_be_merged",
   "subscribed" : true,
-  "user_notes_count": 0
+  "user_notes_count": 0,
+  "should_remove_source_branch": true,
+  "force_remove_source_branch": false
 }
 ```
 
@@ -383,7 +391,9 @@ Parameters:
   "merge_when_build_succeeds": true,
   "merge_status": "can_be_merged",
   "subscribed" : true,
-  "user_notes_count": 1
+  "user_notes_count": 1,
+  "should_remove_source_branch": true,
+  "force_remove_source_branch": false
 }
 ```
 
@@ -481,7 +491,9 @@ Parameters:
   "merge_when_build_succeeds": true,
   "merge_status": "can_be_merged",
   "subscribed" : true,
-  "user_notes_count": 1
+  "user_notes_count": 1,
+  "should_remove_source_branch": true,
+  "force_remove_source_branch": false
 }
 ```
 
@@ -547,7 +559,9 @@ Parameters:
   "merge_when_build_succeeds": true,
   "merge_status": "can_be_merged",
   "subscribed" : true,
-  "user_notes_count": 1
+  "user_notes_count": 1,
+  "should_remove_source_branch": true,
+  "force_remove_source_branch": false
 }
 ```
 
@@ -866,7 +880,9 @@ Example response:
     "merge_when_build_succeeds": false,
     "merge_status": "unchecked",
     "subscribed": true,
-    "user_notes_count": 7
+    "user_notes_count": 7,
+    "should_remove_source_branch": true,
+    "force_remove_source_branch": false
   },
   "target_url": "https://gitlab.example.com/gitlab-org/gitlab-ci/merge_requests/7",
   "body": "Et voluptas laudantium minus nihil recusandae ut accusamus earum aut non.",
diff --git a/doc/api/todos.md b/doc/api/todos.md
index 29e736644107b8840cae47815b6575a950e1759a..23f6e35f2a4879111ae45a686b2f13446b6006bd 100644
--- a/doc/api/todos.md
+++ b/doc/api/todos.md
@@ -15,7 +15,7 @@ Parameters:
 
 | Attribute | Type | Required | Description |
 | --------- | ---- | -------- | ----------- |
-| `action` | string | no | The action to be filtered. Can be `assigned`, `mentioned`, `build_failed`, or `marked`. |
+| `action` | string | no | The action to be filtered. Can be `assigned`, `mentioned`, `build_failed`, `marked`, or `approval_required`. |
 | `author_id` | integer | no | The ID of an author |
 | `project_id` | integer | no | The ID of a project |
 | `state` | string | no | The state of the todo. Can be either `pending` or `done` |
diff --git a/doc/ci/README.md b/doc/ci/README.md
index a9d407528e8a00097a826faaa1e8b16c8a96012b..0833027f91d7789a8bbb5dc916dddea622f0c90a 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -15,6 +15,6 @@
 - [Use SSH keys in your build environment](ssh_keys/README.md)
 - [Trigger builds through the API](triggers/README.md)
 - [Build artifacts](build_artifacts/README.md)
-- [User permissions](permissions/README.md)
+- [User permissions](../user/permissions.md#gitlab-ci)
 - [API](../api/ci/README.md)
 - [CI services (linked docker containers)](services/README.md)
diff --git a/doc/ci/permissions/README.md b/doc/ci/permissions/README.md
index d77061c14cdc324c59f1e70efc9ea194cce65b9d..42eb59f84c898fbd1a8db271f532ebf8d6b4fdc6 100644
--- a/doc/ci/permissions/README.md
+++ b/doc/ci/permissions/README.md
@@ -1,24 +1,3 @@
 # Users Permissions
 
-GitLab CI relies on user's role on the GitLab. There are three permissions levels on GitLab CI: admin, master, developer, other.
-
-Admin user can perform any actions on GitLab CI in scope of instance and project. Also user with admin permission can use admin interface.
-
-
-
-
-| Action                                | Guest, Reporter | Developer   | Master   | Admin  |
-|---------------------------------------|-----------------|-------------|----------|--------|
-| See commits and builds                | ✓               | ✓           | ✓        | ✓      |
-| Retry or cancel build                 |                 | ✓           | ✓        | ✓      |
-| Remove project                        |                 |             | ✓        | ✓      |
-| Create project                        |                 |             | ✓        | ✓      |
-| Change project configuration          |                 |             | ✓        | ✓      |
-| Add specific runners                  |                 |             | ✓        | ✓      |
-| Add shared runners                    |                 |             |          | ✓      |
-| See events in the system              |                 |             |          | ✓      |
-| Admin interface                       |                 |             |          | ✓      |
-
-
-
-
+This document was moved to [user/permissions.md](../../user/permissions.md#gitlab-ci).
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index eb81267242ea8922f562e981d67853207a5c5874..50fa263f6930da0014f7113b90bfb157f832ff77 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -133,7 +133,7 @@ builds, including deploy builds. This can be an array or a multi-line string.
 ### after_script
 
 >**Note:**
-Introduced in GitLab 8.7 and requires Gitlab Runner v1.2 (not yet released)
+Introduced in GitLab 8.7 and requires Gitlab Runner v1.2
 
 `after_script` is used to define the command that will be run after for all
 builds. This has to be an array or a multi-line string.
@@ -985,11 +985,11 @@ directive defined in `.postgres_services` and `.mysql_services` respectively:
     - ruby
 
 test:postgres:
-  << *job_definition
+  <<: *job_definition
   services: *postgres_definition
 
 test:mysql:
-  << *job_definition
+  <<: *job_definition
   services: *mysql_definition
 ```
 
diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md
index 975bb82c37d858979664e2f2faf10a2b9eec80ba..fac35ec964d9b1b527585ee78a0e0b5155b503f2 100644
--- a/doc/development/doc_styleguide.md
+++ b/doc/development/doc_styleguide.md
@@ -44,7 +44,7 @@ it organized and easy to find.
 - When introducing a new document, be careful for the headings to be
   grammatically and syntactically correct. It is advised to mention one or all
   of the following GitLab members for a review: `@axil`, `@rspeicher`,
-  `@dblessing`, `@ashleys`, `@nearlythere`. This is to ensure that no document
+  `@dblessing`, `@ashleys`. This is to ensure that no document
   with wrong heading is going live without an audit, thus preventing dead links
   and redirection issues when corrected
 - Leave exactly one newline after a heading
diff --git a/doc/development/ui_guide.md b/doc/development/ui_guide.md
index ce0aaa2fd253419bceaa0532107477734ac3bf0a..6525228801990c6bca46d8e6f95da0d0c6d352a6 100644
--- a/doc/development/ui_guide.md
+++ b/doc/development/ui_guide.md
@@ -1,43 +1,45 @@
-# UI Guide for building GitLab 
+# UI Guide for building GitLab
 
 ## GitLab UI development kit
 
 We created a page inside GitLab where you can check commonly used html and css elements.
 
-When you run GitLab instance locally - just visit http://localhost:3000/help/ui page to see UI examples 
+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. 
+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 
+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 
+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.
+When adding a new tab to the header don't use more than 2 words for text in the link.
+We want to keep links short and easy to remember and fit all of them in the small screen.
 
-## Mobile screen size 
+## 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. 
+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 
+* `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
@@ -50,8 +52,14 @@ information from database or file system
 
 * 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. 
-* Text button should have only first word capitalized. So should be "Create issue" instead of "Create 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.
+* Text button should have only first word capitalized. So should be "Create issue" instead of "Create Issue"
 
+## Counts
+
+* Always use the [`number_with_delimiter`][number_with_delimiter] helper to
+  display counts in the UI.
+
+[number_with_delimiter]: http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_with_delimiter
diff --git a/doc/integration/oauth_provider.md b/doc/integration/oauth_provider.md
index 5f8bb57365cd247b4db36ca489fa32850a8d568a..0c53584d201c14e50cc0f1941f6879ffa74add16 100644
--- a/doc/integration/oauth_provider.md
+++ b/doc/integration/oauth_provider.md
@@ -28,7 +28,8 @@ GitLab supports two ways of adding a new OAuth2 application to an instance. You
 can either add an application as a regular user or add it in the admin area.
 What this means is that GitLab can actually have instance-wide and a user-wide
 applications. There is no difference between them except for the different
-permission levels they are set (user/admin).
+permission levels they are set (user/admin). The default callback URL is 
+`http://your-gitlab.example.com/users/auth/gitlab/callback`
 
 ## Adding an application through the profile
 
diff --git a/doc/integration/saml.md b/doc/integration/saml.md
index 8a7205caaa46c04a5f8351c067eb29b724445770..f3b2a2887769f6e3c36d24b71c61758743841af0 100644
--- a/doc/integration/saml.md
+++ b/doc/integration/saml.md
@@ -138,7 +138,7 @@ This setting is only available on GitLab 8.7 and above.
 
 SAML login includes support for external groups. You can define in the SAML
 settings which groups, to which your users belong in your IdP, you wish to be
-marked as [external](../permissions/permissions.md).
+marked as [external](../user/permissions.md).
 
 ### Requirements
 
@@ -306,4 +306,4 @@ For this you need take the following into account:
   validators are optional
 
 Make sure that one of the above described scenarios is valid, or the requests will
-fail with one of the mentioned errors.
\ No newline at end of file
+fail with one of the mentioned errors.
diff --git a/doc/permissions/permissions.md b/doc/permissions/permissions.md
index 44f3f6d3b1279f02ff374f5295be65cce84c7a60..78d67aeec7829d7af93adaa99c86137e089326c9 100644
--- a/doc/permissions/permissions.md
+++ b/doc/permissions/permissions.md
@@ -1,104 +1,3 @@
 # Permissions
 
-Users have different abilities depending on the access level they have in a particular group or project.
-
-If a user is both in a project group and in the project itself, the highest permission level is used.
-
-If a user is a GitLab administrator they receive all permissions.
-
-On public and internal projects the Guest role is not enforced.
-All users will be able to create issues, leave comments, and pull or download the project code.
-
-To add or import a user, you can follow the [project users and members
-documentation](../workflow/add-user/add-user.md).
-
-## Project
-
-| Action                                | Guest   | Reporter   | Developer   | Master   | Owner  |
-|---------------------------------------|---------|------------|-------------|----------|--------|
-| Create new issue                      | ✓       | ✓          | ✓           | ✓        | ✓      |
-| Leave comments                        | ✓       | ✓          | ✓           | ✓        | ✓      |
-| See a list of builds                  | ✓ [^1]  | ✓          | ✓           | ✓        | ✓      |
-| See a build log                       | ✓ [^1]  | ✓          | ✓           | ✓        | ✓      |
-| Download and browse build artifacts   | ✓ [^1]  | ✓          | ✓           | ✓        | ✓      |
-| Pull project code                     |         | ✓          | ✓           | ✓        | ✓      |
-| Download project                      |         | ✓          | ✓           | ✓        | ✓      |
-| Create code snippets                  |         | ✓          | ✓           | ✓        | ✓      |
-| Manage issue tracker                  |         | ✓          | ✓           | ✓        | ✓      |
-| Manage labels                         |         | ✓          | ✓           | ✓        | ✓      |
-| See a commit status                   |         | ✓          | ✓           | ✓        | ✓      |
-| See a container registry              |         | ✓          | ✓           | ✓        | ✓      |
-| See environments                      |         | ✓          | ✓           | ✓        | ✓      |
-| Manage merge requests                 |         |            | ✓           | ✓        | ✓      |
-| Create new merge request              |         |            | ✓           | ✓        | ✓      |
-| Create new branches                   |         |            | ✓           | ✓        | ✓      |
-| Push to non-protected branches        |         |            | ✓           | ✓        | ✓      |
-| Force push to non-protected branches  |         |            | ✓           | ✓        | ✓      |
-| Remove non-protected branches         |         |            | ✓           | ✓        | ✓      |
-| Add tags                              |         |            | ✓           | ✓        | ✓      |
-| Write a wiki                          |         |            | ✓           | ✓        | ✓      |
-| Cancel and retry builds               |         |            | ✓           | ✓        | ✓      |
-| 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            |         |            |             | ✓        | ✓      |
-| Enable/disable branch protection      |         |            |             | ✓        | ✓      |
-| Turn on/off prot. branch push for devs|         |            |             | ✓        | ✓      |
-| Rewrite/remove git tags               |         |            |             | ✓        | ✓      |
-| Edit project                          |         |            |             | ✓        | ✓      |
-| Add deploy keys to project            |         |            |             | ✓        | ✓      |
-| Configure project hooks               |         |            |             | ✓        | ✓      |
-| Manage runners                        |         |            |             | ✓        | ✓      |
-| Manage build triggers                 |         |            |             | ✓        | ✓      |
-| Manage variables                      |         |            |             | ✓        | ✓      |
-| Delete environments                   |         |            |             | ✓        | ✓      |
-| Switch visibility level               |         |            |             |          | ✓      |
-| Transfer project to another namespace |         |            |             |          | ✓      |
-| Remove project                        |         |            |             |          | ✓      |
-| Force push to protected branches [^2] |         |            |             |          |        |
-| Remove protected branches [^2]        |         |            |             |          |        |
-
-[^1]: If **Allow guest to access builds** is enabled in CI settings
-[^2]: Not allowed for Guest, Reporter, Developer, Master, or Owner
-
-## Group
-
-In order for a group to appear as public and be browsable, it must contain at
-least one public project.
-
-Any user can remove themselves from a group, unless they are the last Owner of the group.
-
-| Action                  | Guest | Reporter | Developer | Master | Owner |
-|-------------------------|-------|----------|-----------|--------|-------|
-| Browse group            | ✓     | ✓        | ✓         | ✓      | ✓     |
-| Edit group              |       |          |           |        | ✓     |
-| Create project in group |       |          |           | ✓      | ✓     |
-| Manage group members    |       |          |           |        | ✓     |
-| Remove group            |       |          |           |        | ✓     |
-
-## External Users
-
-In cases where it is desired that a user has access only to some internal or
-private projects, there is the option of creating **External Users**. This
-feature may be useful when for example a contractor is working on a given
-project and should only have access to that project.
-
-External users can only access projects to which they are explicitly granted
-access, thus hiding all other internal or private ones from them. Access can be
-granted by adding the user as member to the project or group.
-
-They will, like usual users, receive a role in the project or group with all
-the abilities that are mentioned in the table above. They cannot however create
-groups or projects, and they have the same access as logged out users in all
-other cases.
-
-An administrator can flag a user as external [through the API](../api/users.md)
-or by checking the checkbox on the admin panel. As an administrator, navigate
-to **Admin > Users** to create a new user or edit an existing one. There, you
-will find the option to flag the user as external.
-
-By default new users are not set as external users. This behavior can be changed
-by an administrator under **Admin > Application Settings**.
\ No newline at end of file
+This document was moved to [user/permissions.md](../user/permissions.md).
diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md
index 9a5c5a5c92a07fe4be7df16e3e3b92bb13b9e61e..a3921f1b89f32af853f9984b895236621e30aa45 100644
--- a/doc/public_access/public_access.md
+++ b/doc/public_access/public_access.md
@@ -17,7 +17,7 @@ Public projects can be cloned **without any** authentication.
 
 They will also be listed on the public access directory (`/public`).
 
-**Any logged in user** will have [Guest](../permissions/permissions.md)
+**Any logged in user** will have [Guest](../user/permissions.md)
 permissions on the repository.
 
 ### Internal projects
@@ -27,7 +27,7 @@ Internal projects can be cloned by any logged in user.
 They will also be listed on the public access directory (`/public`) for logged
 in users.
 
-Any logged in user will have [Guest](../permissions/permissions.md) permissions
+Any logged in user will have [Guest](../user/permissions.md) permissions
 on the repository.
 
 ### How to change project visibility
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
new file mode 100644
index 0000000000000000000000000000000000000000..665428617816f388698307e91e187df9910e1d50
--- /dev/null
+++ b/doc/user/permissions.md
@@ -0,0 +1,131 @@
+# Permissions
+
+Users have different abilities depending on the access level they have in a
+particular group or project. If a user is both in a group's project and the
+project itself, the highest permission level is used.
+
+On public and internal projects the Guest role is not enforced. All users will
+be able to create issues, leave comments, and pull or download the project code.
+
+GitLab administrators receive all permissions.
+
+To add or import a user, you can follow the [project users and members
+documentation](../workflow/add-user/add-user.md).
+
+## Project
+
+The following table depicts the various user permission levels in a project.
+
+| Action                                | Guest   | Reporter   | Developer   | Master   | Owner  |
+|---------------------------------------|---------|------------|-------------|----------|--------|
+| Create new issue                      | ✓       | ✓          | ✓           | ✓        | ✓      |
+| Leave comments                        | ✓       | ✓          | ✓           | ✓        | ✓      |
+| See a list of builds                  | ✓ [^1]  | ✓          | ✓           | ✓        | ✓      |
+| See a build log                       | ✓ [^1]  | ✓          | ✓           | ✓        | ✓      |
+| Download and browse build artifacts   | ✓ [^1]  | ✓          | ✓           | ✓        | ✓      |
+| Pull project code                     |         | ✓          | ✓           | ✓        | ✓      |
+| Download project                      |         | ✓          | ✓           | ✓        | ✓      |
+| Create code snippets                  |         | ✓          | ✓           | ✓        | ✓      |
+| Manage issue tracker                  |         | ✓          | ✓           | ✓        | ✓      |
+| Manage labels                         |         | ✓          | ✓           | ✓        | ✓      |
+| See a commit status                   |         | ✓          | ✓           | ✓        | ✓      |
+| See a container registry              |         | ✓          | ✓           | ✓        | ✓      |
+| See environments                      |         | ✓          | ✓           | ✓        | ✓      |
+| Manage/Accept merge requests          |         |            | ✓           | ✓        | ✓      |
+| Create new merge request              |         |            | ✓           | ✓        | ✓      |
+| Create new branches                   |         |            | ✓           | ✓        | ✓      |
+| Push to non-protected branches        |         |            | ✓           | ✓        | ✓      |
+| Force push to non-protected branches  |         |            | ✓           | ✓        | ✓      |
+| Remove non-protected branches         |         |            | ✓           | ✓        | ✓      |
+| Add tags                              |         |            | ✓           | ✓        | ✓      |
+| Write a wiki                          |         |            | ✓           | ✓        | ✓      |
+| Cancel and retry builds               |         |            | ✓           | ✓        | ✓      |
+| 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            |         |            |             | ✓        | ✓      |
+| Enable/disable branch protection      |         |            |             | ✓        | ✓      |
+| Turn on/off protected branch push for devs|         |            |             | ✓        | ✓      |
+| Rewrite/remove Git tags               |         |            |             | ✓        | ✓      |
+| Edit project                          |         |            |             | ✓        | ✓      |
+| Add deploy keys to project            |         |            |             | ✓        | ✓      |
+| Configure project hooks               |         |            |             | ✓        | ✓      |
+| Manage runners                        |         |            |             | ✓        | ✓      |
+| Manage build triggers                 |         |            |             | ✓        | ✓      |
+| Manage variables                      |         |            |             | ✓        | ✓      |
+| Delete environments                   |         |            |             | ✓        | ✓      |
+| Switch visibility level               |         |            |             |          | ✓      |
+| Transfer project to another namespace |         |            |             |          | ✓      |
+| Remove project                        |         |            |             |          | ✓      |
+| Force push to protected branches [^2] |         |            |             |          |        |
+| Remove protected branches [^2]        |         |            |             |          |        |
+
+[^1]: If **Allow guest to access builds** is enabled in CI settings
+[^2]: Not allowed for Guest, Reporter, Developer, Master, or Owner
+
+## Group
+
+Any user can remove themselves from a group, unless they are the last Owner of
+the group. The following table depicts the various user permission levels in a
+group.
+
+| Action                  | Guest | Reporter | Developer | Master | Owner |
+|-------------------------|-------|----------|-----------|--------|-------|
+| Browse group            | ✓     | ✓        | ✓         | ✓      | ✓     |
+| Edit group              |       |          |           |        | ✓     |
+| Create project in group |       |          |           | ✓      | ✓     |
+| Manage group members    |       |          |           |        | ✓     |
+| Remove group            |       |          |           |        | ✓     |
+
+## External Users
+
+In cases where it is desired that a user has access only to some internal or
+private projects, there is the option of creating **External Users**. This
+feature may be useful when for example a contractor is working on a given
+project and should only have access to that project.
+
+External users can only access projects to which they are explicitly granted
+access, thus hiding all other internal or private ones from them. Access can be
+granted by adding the user as member to the project or group.
+
+They will, like usual users, receive a role in the project or group with all
+the abilities that are mentioned in the table above. They cannot however create
+groups or projects, and they have the same access as logged out users in all
+other cases.
+
+An administrator can flag a user as external [through the API](../api/users.md)
+or by checking the checkbox on the admin panel. As an administrator, navigate
+to **Admin > Users** to create a new user or edit an existing one. There, you
+will find the option to flag the user as external.
+
+By default new users are not set as external users. This behavior can be changed
+by an administrator under **Admin > Application Settings**.
+
+## GitLab CI
+
+GitLab CI permissions rely on the role the user has in GitLab. There are four
+permission levels it total:
+
+- admin
+- master
+- developer
+- guest/reporter
+
+The admin user can perform any action on GitLab CI in scope of the GitLab
+instance and project. In addition, all admins can use the admin interface under
+`/admin/runners`.
+
+| Action                                | Guest, Reporter | Developer   | Master   | Admin  |
+|---------------------------------------|-----------------|-------------|----------|--------|
+| See commits and builds                | ✓               | ✓           | ✓        | ✓      |
+| Retry or cancel build                 |                 | ✓           | ✓        | ✓      |
+| Remove project                        |                 |             | ✓        | ✓      |
+| Create project                        |                 |             | ✓        | ✓      |
+| Change project configuration          |                 |             | ✓        | ✓      |
+| Add specific runners                  |                 |             | ✓        | ✓      |
+| Add shared runners                    |                 |             |          | ✓      |
+| See events in the system              |                 |             |          | ✓      |
+| Admin interface                       |                 |             |          | ✓      |
diff --git a/doc/workflow/add-user/add-user.md b/doc/workflow/add-user/add-user.md
index 4b55113025512b724a98d4e2a5bb143ed42a0c68..0537ce0bcd4989ac313098ce14515bb56b505173 100644
--- a/doc/workflow/add-user/add-user.md
+++ b/doc/workflow/add-user/add-user.md
@@ -23,7 +23,7 @@ want to add.
 
 ---
 
-Select the user and the [permission level](../../permissions/permissions.md)
+Select the user and the [permission level](../../user/permissions.md)
 that you'd like to give the user. Note that you can select more than one user.
 
 ![Give user permissions](img/add_user_give_permissions.png)
diff --git a/doc/workflow/forking_workflow.md b/doc/workflow/forking_workflow.md
index 217a4a4012f44257e03eae26add800a5d0926e4e..733d079bd4a015c8f74071282f1c8ed27b549300 100644
--- a/doc/workflow/forking_workflow.md
+++ b/doc/workflow/forking_workflow.md
@@ -38,7 +38,7 @@ Forking a project is in most cases a two-step process.
     ---
 
 After the forking is done, you can start working on the newly created
-repository. There, you will have full [Owner](../permissions/permissions.md)
+repository. There, you will have full [Owner](../user/permissions.md)
 access, so you can set it up as you please.
 
 ## Merging upstream
diff --git a/doc/workflow/protected_branches.md b/doc/workflow/protected_branches.md
index 67adfc2f43aff731517a9898ea701d55e32bdd71..5c1c7b47c8a7023836c7bfd1921288327321f68e 100644
--- a/doc/workflow/protected_branches.md
+++ b/doc/workflow/protected_branches.md
@@ -12,7 +12,7 @@ A protected branch does three simple things:
 
 You can make any branch a protected branch. GitLab makes the master branch a protected branch by default.
 
-To protect a branch, user needs to have at least a Master permission level, see [permissions document](../permissions/permissions.md).
+To protect a branch, user needs to have at least a Master permission level, see [permissions document](../user/permissions.md).
 
 ![protected branches page](protected_branches/protected_branches1.png)
 
diff --git a/features/project/commits/commits.feature b/features/project/commits/commits.feature
index a95df03835738bd06121b0b448998105513d7d80..8b0cb90765eccd30808ac6dfcbd1938db6cec970 100644
--- a/features/project/commits/commits.feature
+++ b/features/project/commits/commits.feature
@@ -83,11 +83,6 @@ Feature: Project Commits
     #Given I visit my project's commits stats page
     #Then I see commits stats
 
-  Scenario: I browse big commit
-    Given I visit big commit page
-    Then I see big commit warning
-    And I see "Reload with full diff" link
-
   Scenario: I browse a commit with an image
     Given I visit a commit with an image that changed
     Then The diff links to both the previous and current image
diff --git a/features/project/commits/diff_comments.feature b/features/project/commits/diff_comments.feature
index 2bde4c8a99ba518fa59b39fbbfbb2038ac369335..35687aac9eac3a74dee074772cfcc71ece5e87a3 100644
--- a/features/project/commits/diff_comments.feature
+++ b/features/project/commits/diff_comments.feature
@@ -5,10 +5,6 @@ Feature: Project Commits Diff Comments
     And I own project "Shop"
     And I visit project commit page
 
-  @javascript
-  Scenario: I can access add diff comment buttons
-    Then I should see add a diff comment button
-
   @javascript
   Scenario: I can comment on a commit diff
     Given I leave a diff comment like "Typo, please fix"
diff --git a/features/steps/dashboard/help.rb b/features/steps/dashboard/help.rb
index 800e869533e927f12fb2bd99e1db30535e69f30e..9c94dc70df0bda915ec42b9d9e3cd0366c265dff 100644
--- a/features/steps/dashboard/help.rb
+++ b/features/steps/dashboard/help.rb
@@ -8,7 +8,7 @@ class Spinach::Features::DashboardHelp < Spinach::FeatureSteps
   end
 
   step 'I visit the "Rake Tasks" help page' do
-    visit help_page_path("raketasks", "maintenance")
+    visit help_page_path("raketasks/maintenance")
   end
 
   step 'I should see "Rake Tasks" page markdown rendered' do
diff --git a/features/steps/project/builds/artifacts.rb b/features/steps/project/builds/artifacts.rb
index 2876e8812e9e319db61554f0b0361a941cf7388f..b4a32ed2e38782af3248bc9d5247e560ca31ecd9 100644
--- a/features/steps/project/builds/artifacts.rb
+++ b/features/steps/project/builds/artifacts.rb
@@ -68,10 +68,16 @@ class Spinach::Features::ProjectBuildsArtifacts < Spinach::FeatureSteps
   end
 
   step 'download of a file extracted from build artifacts should start' do
-    # this will be accelerated by Workhorse
-    response_json = JSON.parse(page.body, symbolize_names: true)
-    expect(response_json[:archive]).to end_with('build_artifacts.zip')
-    expect(response_json[:entry]).to eq Base64.encode64('ci_artifacts.txt')
+    send_data = response_headers[Gitlab::Workhorse::SEND_DATA_HEADER]
+
+    expect(send_data).to start_with('artifacts-entry:')
+
+    base64_params = send_data.sub(/\Aartifacts\-entry:/, '')
+    params = JSON.parse(Base64.urlsafe_decode64(base64_params))
+
+    expect(params.keys).to eq(['Archive', 'Entry'])
+    expect(params['Archive']).to end_with('build_artifacts.zip')
+    expect(params['Entry']).to eq(Base64.encode64('ci_artifacts.txt'))
   end
 
   step 'I click a first row within build artifacts table' do
diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb
index 239036e431d384b7017a495616db97beea3c40cd..bea9f9d198b5b0b34e5a5161de8048227f095838 100644
--- a/features/steps/project/commits/commits.rb
+++ b/features/steps/project/commits/commits.rb
@@ -125,25 +125,6 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
     expect(page).to have_content 'Authors'
   end
 
-  step 'I visit big commit page' do
-    # Create a temporary scope to ensure that the stub_const is removed after user
-    RSpec::Mocks.with_temporary_scope do
-      stub_const('Gitlab::Git::DiffCollection::DEFAULT_LIMITS', { max_lines: 1, max_files: 1 })
-      visit namespace_project_commit_path(@project.namespace, @project, sample_big_commit.id)
-    end
-  end
-
-  step 'I see big commit warning' do
-    expect(page).to have_content sample_big_commit.message
-    expect(page).to have_content "Too many changes"
-  end
-
-  step 'I see "Reload with full diff" link' do
-    link = find_link('Reload with full diff')
-    expect(link[:href]).to end_with('?force_show_diff=true')
-    expect(link[:href]).not_to include('.html')
-  end
-
   step 'I visit a commit with an image that changed' do
     visit namespace_project_commit_path(@project.namespace, @project, sample_image_commit.id)
   end
diff --git a/features/steps/shared/diff_note.rb b/features/steps/shared/diff_note.rb
index 56ef44ec96919631bfcd2755aaf84700ab96ad03..4df4e89f5b98a33c1bfd7caf347b6908f9b21b74 100644
--- a/features/steps/shared/diff_note.rb
+++ b/features/steps/shared/diff_note.rb
@@ -25,17 +25,16 @@ module SharedDiffNote
 
       page.within("form[data-line-code='#{sample_commit.line_code}']") do
         fill_in "note[note]", with: "Typo, please fix"
-        find(".js-comment-button").trigger("click")
-        sleep 0.05
+        find(".js-comment-button").click
       end
     end
   end
 
   step 'I leave a diff comment in a parallel view on the left side like "Old comment"' do
-    click_parallel_diff_line(sample_commit.line_code, 'old')
-    page.within("#{diff_file_selector} form[data-line-code='#{sample_commit.line_code}']") do
+    click_parallel_diff_line(sample_commit.del_line_code, 'old')
+    page.within("#{diff_file_selector} form[data-line-code='#{sample_commit.del_line_code}']") do
       fill_in "note[note]", with: "Old comment"
-      find(".js-comment-button").trigger("click")
+      find(".js-comment-button").click
     end
   end
 
@@ -43,7 +42,7 @@ module SharedDiffNote
     click_parallel_diff_line(sample_commit.line_code, 'new')
     page.within("#{diff_file_selector} form[data-line-code='#{sample_commit.line_code}']") do
       fill_in "note[note]", with: "New comment"
-      find(".js-comment-button").trigger("click")
+      find(".js-comment-button").click
     end
   end
 
@@ -165,10 +164,6 @@ module SharedDiffNote
     end
   end
 
-  step 'I should see add a diff comment button' do
-    expect(page).to have_css('.js-add-diff-note-button')
-  end
-
   step 'I should see an empty diff comment form' do
     page.within(diff_file_selector) do
       expect(page).to have_field("note[note]", with: "")
@@ -215,7 +210,7 @@ module SharedDiffNote
   end
 
   step 'I click side-by-side diff button' do
-    find('#parallel-diff-btn').trigger('click')
+    find('#parallel-diff-btn').click
   end
 
   step 'I see side-by-side diff button' do
@@ -227,10 +222,12 @@ module SharedDiffNote
   end
 
   def click_diff_line(code)
-    find("button[data-line-code='#{code}']").trigger('click')
+    find(".line_holder[id='#{code}'] td:nth-of-type(1)").trigger 'mouseover'
+    find(".line_holder[id='#{code}'] button").trigger 'click'
   end
 
   def click_parallel_diff_line(code, line_type)
-    find("button[data-line-code='#{code}'][data-line-type='#{line_type}']").trigger('click')
+    find(".line_content.parallel.#{line_type}[data-line-code='#{code}']").trigger 'mouseover'
+    find(".line_holder.parallel button[data-line-code='#{code}']").trigger 'click'
   end
 end
diff --git a/features/support/env.rb b/features/support/env.rb
index ab3f0ca7aeb8d70beb684c64358f0028c3fa26ea..f0a3dd8d2d0c620330a669f1b38724d9981e568d 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -13,7 +13,7 @@ require_relative 'rerun'
 
 if ENV['CI']
   require 'knapsack'
-  Knapsack::Adapters::RSpecAdapter.bind
+  Knapsack::Adapters::SpinachAdapter.bind
 end
 
 %w(select2_helper test_env repo_helpers).each do |f|
diff --git a/lib/api/award_emoji.rb b/lib/api/award_emoji.rb
index c4fa1838b5a4bb1e3db6f0cf4eeedffcf983a477..2efe7e3adf3474888034daa4a7233148d190afd5 100644
--- a/lib/api/award_emoji.rb
+++ b/lib/api/award_emoji.rb
@@ -56,9 +56,9 @@ module API
 
             not_found!('Award Emoji') unless can_read_awardable?
 
-            award = awardable.award_emoji.new(name: params[:name], user: current_user)
+            award = awardable.create_award_emoji(params[:name], current_user)
 
-            if award.save
+            if award.persisted?
               present award, with: Entities::AwardEmoji
             else
               not_found!("Award Emoji #{award.errors.messages}")
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index 323a70868900afd1277eada973162f2a70cb8375..acb4812b5cf5ab22d65e5da017c2ed08e912cf30 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -64,7 +64,7 @@ module API
           ref = branches.first
         end
 
-        pipeline = @project.ensure_pipeline(commit.sha, ref)
+        pipeline = @project.ensure_pipeline(commit.sha, ref, current_user)
 
         name = params[:name] || params[:context]
         status = GenericCommitStatus.running_or_pending.find_by(pipeline: pipeline, name: name, ref: params[:ref])
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 9076a0c3831b033112d1047269e2ac188823b708..3c79a00eb8c89093ef1c952b5d00df3319939e55 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -54,6 +54,7 @@ module API
 
     class BasicProjectDetails < Grape::Entity
       expose :id
+      expose :http_url_to_repo, :web_url
       expose :name, :name_with_namespace
       expose :path, :path_with_namespace
     end
@@ -186,6 +187,7 @@ module API
       end
       expose :user_notes_count
       expose :upvotes, :downvotes
+      expose :due_date
     end
 
     class ExternalIssue < Grape::Entity
@@ -199,7 +201,6 @@ module API
       expose :author, :assignee, using: Entities::UserBasic
       expose :source_project_id, :target_project_id
       expose :label_names, as: :labels
-      expose :description
       expose :work_in_progress?, as: :work_in_progress
       expose :milestone, using: Entities::Milestone
       expose :merge_when_build_succeeds
@@ -208,6 +209,8 @@ module API
         merge_request.subscribed?(options[:current_user])
       end
       expose :user_notes_count
+      expose :should_remove_source_branch?, as: :should_remove_source_branch
+      expose :force_remove_source_branch?, as: :force_remove_source_branch
     end
 
     class MergeRequestChanges < MergeRequest
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index d5dfba5e0cc989246613daa82ea5b3618f801f96..959b700de782aee89412bb3d53119786d3127cab 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -63,7 +63,12 @@ module API
         if access_status.status
           # Return the repository full path so that gitlab-shell has it when
           # handling ssh commands
-          response[:repository_path] = project.repository.path_to_repo
+          response[:repository_path] =
+            if wiki?
+              project.wiki.repository.path_to_repo
+            else
+              project.repository.path_to_repo
+            end
         end
 
         response
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 8a03a41e9c592345685ce44e96375a82923387f6..c588103e517eb05894ba94b7d0d9292786004a09 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -152,12 +152,13 @@ module API
       #   milestone_id (optional) - The ID of a milestone to assign issue
       #   labels (optional)       - The labels of an issue
       #   created_at (optional)   - Date time string, ISO 8601 formatted
+      #   due_date (optional)     - Date time string in the format YEAR-MONTH-DAY
       # Example Request:
       #   POST /projects/:id/issues
-      post ":id/issues" do
+      post ':id/issues' do
         required_attributes! [:title]
 
-        keys = [:title, :description, :assignee_id, :milestone_id]
+        keys = [:title, :description, :assignee_id, :milestone_id, :due_date]
         keys << :created_at if current_user.admin? || user_project.owner == current_user
         attrs = attributes_for_keys(keys)
 
@@ -201,12 +202,13 @@ module API
       #   labels (optional) - The labels of an issue
       #   state_event (optional) - The state event of an issue (close|reopen)
       #   updated_at (optional) - Date time string, ISO 8601 formatted
+      #   due_date (optional)     - Date time string in the format YEAR-MONTH-DAY
       # Example Request:
       #   PUT /projects/:id/issues/:issue_id
-      put ":id/issues/:issue_id" do
+      put ':id/issues/:issue_id' do
         issue = user_project.issues.find(params[:issue_id])
         authorize! :update_issue, issue
-        keys = [:title, :description, :assignee_id, :milestone_id, :state_event]
+        keys = [:title, :description, :assignee_id, :milestone_id, :state_event, :due_date]
         keys << :updated_at if current_user.admin? || user_project.owner == current_user
         attrs = attributes_for_keys(keys)
 
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 0cc1edd65c83566d96b91a83be01c3fdec70de68..6d2a6f3946c45644a95ac073fdd355104517b1ff 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -25,7 +25,11 @@ module API
         @projects = current_user.authorized_projects
         @projects = filter_projects(@projects)
         @projects = paginate @projects
-        present @projects, with: Entities::ProjectWithAccess, user: current_user
+        if params[:simple]
+          present @projects, with: Entities::BasicProjectDetails, user: current_user
+        else
+          present @projects, with: Entities::ProjectWithAccess, user: current_user
+        end
       end
 
       # Get an owned projects list for authenticated user
diff --git a/lib/banzai.rb b/lib/banzai.rb
index 093382261ae8552645b1c3cd2cb44ce8c0bb3538..9ebe379f454e04691b212b439984ef0c34174013 100644
--- a/lib/banzai.rb
+++ b/lib/banzai.rb
@@ -3,6 +3,10 @@ module Banzai
     Renderer.render(text, context)
   end
 
+  def self.cache_collection_render(texts_and_contexts)
+    Renderer.cache_collection_render(texts_and_contexts)
+  end
+
   def self.render_result(text, context = {})
     Renderer.render_result(text, context)
   end
diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb
index 536b478979f78ad64babe872bf3f1baf18d4f199..91f0159f9a1180e13824da5c0668202a509d115b 100644
--- a/lib/banzai/filter/syntax_highlight_filter.rb
+++ b/lib/banzai/filter/syntax_highlight_filter.rb
@@ -19,14 +19,22 @@ module Banzai
         language = node.attr('class')
         code     = node.text
 
+        css_classes = "code highlight"
+
+        lexer = Rouge::Lexer.find_fancy(language) || Rouge::Lexers::PlainText
+        formatter = Rouge::Formatters::HTML.new
+
         begin
-          highlighted = block_code(code, language)
+          code = formatter.format(lexer.lex(code))
+
+          css_classes << " js-syntax-highlight #{lexer.tag}"
         rescue
           # Gracefully handle syntax highlighter bugs/errors to ensure
           # users can still access an issue/comment/etc.
-          highlighted = "<pre>#{code}</pre>"
         end
 
+        highlighted = %(<pre class="#{css_classes}"><code>#{code}</code></pre>)
+
         # Extracted to a method to measure it
         replace_parent_pre_element(node, highlighted)
       end
@@ -40,8 +48,7 @@ module Banzai
 
       # Override Rouge::Plugins::Redcarpet#rouge_formatter
       def rouge_formatter(lexer)
-        Rouge::Formatters::HTMLGitlab.new(
-          cssclass: "code highlight js-syntax-highlight #{lexer.tag}")
+        Rouge::Formatters::HTML.new
       end
     end
   end
diff --git a/lib/banzai/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb
index 5b0a6d8541b0edd636394ee64f9e3d3de817dc11..e1ca7f4d24b8f8e9da7bb579d07fa9dd144b14ab 100644
--- a/lib/banzai/filter/user_reference_filter.rb
+++ b/lib/banzai/filter/user_reference_filter.rb
@@ -112,7 +112,7 @@ module Banzai
         data = data_attribute(project: project.id, author: author.try(:id))
         text = link_text || User.reference_prefix + 'all'
 
-        link_tag(url, data, text)
+        link_tag(url, data, text, 'All Project and Group Members')
       end
 
       def link_to_namespace(namespace, link_text: nil)
@@ -128,7 +128,7 @@ module Banzai
         data = data_attribute(group: namespace.id)
         text = link_text || Group.reference_prefix + group
 
-        link_tag(url, data, text)
+        link_tag(url, data, text, namespace.name)
       end
 
       def link_to_user(user, namespace, link_text: nil)
@@ -136,11 +136,11 @@ module Banzai
         data = data_attribute(user: namespace.owner_id)
         text = link_text || User.reference_prefix + user
 
-        link_tag(url, data, text)
+        link_tag(url, data, text, namespace.owner_name)
       end
 
-      def link_tag(url, data, text)
-        %(<a href="#{url}" #{data} class="#{link_class}">#{escape_once(text)}</a>)
+      def link_tag(url, data, text, title)
+        %(<a href="#{url}" #{data} class="#{link_class}" title="#{escape_once(title)}">#{escape_once(text)}</a>)
       end
     end
   end
diff --git a/lib/banzai/object_renderer.rb b/lib/banzai/object_renderer.rb
index f0e4f28bf12312c67fa12472a5e9f7e8876a642b..9aef807c1528d2b66658fc5b68dd82acbf741504 100644
--- a/lib/banzai/object_renderer.rb
+++ b/lib/banzai/object_renderer.rb
@@ -31,17 +31,15 @@ module Banzai
       redacted = redact_documents(documents)
 
       objects.each_with_index do |object, index|
-        object.__send__("#{attribute}_html=", redacted.fetch(index))
+        redacted_data = redacted[index]
+        object.__send__("#{attribute}_html=", redacted_data[:document].to_html.html_safe)
+        object.user_visible_reference_count = redacted_data[:visible_reference_count]
       end
-
-      objects
     end
 
     # Renders the attribute of every given object.
     def render_objects(objects, attribute)
-      objects.map do |object|
-        render_attribute(object, attribute)
-      end
+      render_attributes(objects, attribute)
     end
 
     # Redacts the list of documents.
@@ -50,9 +48,7 @@ module Banzai
     def redact_documents(documents)
       redactor = Redactor.new(project, user)
 
-      redactor.redact(documents).map do |document|
-        document.to_html.html_safe
-      end
+      redactor.redact(documents)
     end
 
     # Returns a Banzai context for the given object and attribute.
@@ -66,16 +62,21 @@ module Banzai
       context
     end
 
-    # Renders the attribute of an object.
+    # Renders the attributes of a set of objects.
     #
-    # Returns a `Nokogiri::HTML::Document`.
-    def render_attribute(object, attribute)
-      context = context_for(object, attribute)
+    # Returns an Array of `Nokogiri::HTML::Document`.
+    def render_attributes(objects, attribute)
+      strings_and_contexts = objects.map do |object|
+        context = context_for(object, attribute)
 
-      string = object.__send__(attribute)
-      html = Banzai.render(string, context)
+        string = object.__send__(attribute)
 
-      Banzai::Pipeline[:relative_link].to_document(html, context)
+        { text: string, context: context }
+      end
+
+      Banzai.cache_collection_render(strings_and_contexts).each_with_index.map do |html, index|
+        Banzai::Pipeline[:relative_link].to_document(html, strings_and_contexts[index][:context])
+      end
     end
 
     def base_context
diff --git a/lib/banzai/redactor.rb b/lib/banzai/redactor.rb
index ffd267d5e9ae48421f8868c1099d73fc0d670363..0df3a72d1c46d3395ea7d9744cf5c93a5c4d004a 100644
--- a/lib/banzai/redactor.rb
+++ b/lib/banzai/redactor.rb
@@ -19,29 +19,36 @@ module Banzai
     #
     # Returns the documents passed as the first argument.
     def redact(documents)
-      nodes = documents.flat_map do |document|
-        Querying.css(document, 'a.gfm[data-reference-type]')
-      end
-
-      redact_nodes(nodes)
+      all_document_nodes = document_nodes(documents)
 
-      documents
+      redact_document_nodes(all_document_nodes)
     end
 
-    # Redacts the given nodes
+    # Redacts the given node documents
     #
-    # nodes - An Array of HTML nodes to redact.
-    def redact_nodes(nodes)
-      visible = nodes_visible_to_user(nodes)
+    # data - An Array of a Hashes mapping an HTML document to nodes to redact.
+    def redact_document_nodes(all_document_nodes)
+      all_nodes = all_document_nodes.map { |x| x[:nodes] }.flatten
+      visible = nodes_visible_to_user(all_nodes)
+      metadata = []
 
-      nodes.each do |node|
-        unless visible.include?(node)
+      all_document_nodes.each do |entry|
+        nodes_for_document = entry[:nodes]
+        doc_data = { document: entry[:document], visible_reference_count: nodes_for_document.count }
+        metadata << doc_data
+
+        nodes_for_document.each do |node|
+          next if visible.include?(node)
+
+          doc_data[:visible_reference_count] -= 1
           # 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
           node.replace(text)
         end
       end
+
+      metadata
     end
 
     # Returns the nodes visible to the current user.
@@ -65,5 +72,11 @@ module Banzai
 
       visible
     end
+
+    def document_nodes(documents)
+      documents.map do |document|
+        { document: document, nodes: Querying.css(document, 'a.gfm[data-reference-type]') }
+      end
+    end
   end
 end
diff --git a/lib/banzai/renderer.rb b/lib/banzai/renderer.rb
index 6718acdef7e017fc304748fb771b0bf05424e7d4..910687a7b6a33c0db68cfcd4d796701e801d01b9 100644
--- a/lib/banzai/renderer.rb
+++ b/lib/banzai/renderer.rb
@@ -10,7 +10,7 @@ module Banzai
     # requiring XHTML, such as Atom feeds, need to call `post_process` on the
     # result, providing the appropriate `pipeline` option.
     #
-    # markdown - Markdown String
+    # text     - Markdown String
     # context  - Hash of context options passed to our HTML Pipeline
     #
     # Returns an HTML-safe String
@@ -29,6 +29,58 @@ module Banzai
       end
     end
 
+    # Perform multiple render from an Array of Markdown String into an
+    # Array of HTML-safe String of HTML.
+    #
+    # As the rendered Markdown String can be already cached read all the data
+    # from the cache using Rails.cache.read_multi operation. If the Markdown String
+    # is not in the cache or it's not cacheable (no cache_key entry is provided in
+    # the context) the Markdown String is rendered and stored in the cache so the
+    # next render call gets the rendered HTML-safe String from the cache.
+    #
+    # For further explanation see #render method comments.
+    #
+    # texts_and_contexts - An Array of Hashes that contains the Markdown String (:text)
+    #                      an options passed to our HTML Pipeline (:context)
+    #
+    # If on the :context you specify a :cache_key entry will be used to retrieve it
+    # and cache the result of rendering the Markdown String.
+    #
+    # Returns an Array containing HTML-safe String instances.
+    #
+    # Example:
+    #    texts_and_contexts
+    #    => [{ text: '### Hello',
+    #          context: { cache_key: [note, :note] } }]
+    def self.cache_collection_render(texts_and_contexts)
+      items_collection = texts_and_contexts.each_with_index do |item, index|
+        context = item[:context]
+        cache_key = full_cache_multi_key(context.delete(:cache_key), context[:pipeline])
+
+        item[:cache_key] = cache_key if cache_key
+      end
+
+      cacheable_items, non_cacheable_items = items_collection.partition { |item| item.key?(:cache_key) }
+
+      items_in_cache = []
+      items_not_in_cache = []
+
+      unless cacheable_items.empty?
+        items_in_cache = Rails.cache.read_multi(*cacheable_items.map { |item| item[:cache_key] })
+        items_not_in_cache = cacheable_items.reject do |item|
+          item[:rendered] = items_in_cache[item[:cache_key]]
+          items_in_cache.key?(item[:cache_key])
+        end
+      end
+
+      (items_not_in_cache + non_cacheable_items).each do |item|
+        item[:rendered] = render(item[:text], item[:context])
+        Rails.cache.write(item[:cache_key], item[:rendered]) if item[:cache_key]
+      end
+
+      items_collection.map { |item| item[:rendered] }
+    end
+
     def self.render_result(text, context = {})
       text = Pipeline[:pre_process].to_html(text, context) if text
 
@@ -78,5 +130,13 @@ module Banzai
       return unless cache_key
       ["banzai", *cache_key, pipeline_name || :full]
     end
+
+    # To map Rails.cache.read_multi results we need to know the Rails.cache.expanded_key.
+    # Other option will be to generate stringified keys on our side and don't delegate to Rails.cache.expanded_key
+    # method.
+    def self.full_cache_multi_key(cache_key, pipeline_name)
+      return unless cache_key
+      Rails.cache.send(:expanded_key, full_cache_key(cache_key, pipeline_name))
+    end
   end
 end
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index 01ef13df57ab4fc33c814e98bc854f5c96beadf8..a48dc542b14c35e87bfa1809bb70ba872d78c2ea 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -31,28 +31,34 @@ module Ci
       raise ValidationError, e.message
     end
 
-    def builds_for_stage_and_ref(stage, ref, tag = false, trigger_request = nil)
-      builds.select do |build|
-        build[:stage] == stage &&
-          process?(build[:only], build[:except], ref, tag, trigger_request)
+    def jobs_for_ref(ref, tag = false, trigger_request = nil)
+      @jobs.select do |_, job|
+        process?(job[:only], job[:except], ref, tag, trigger_request)
       end
     end
 
-    def builds
-      @jobs.map do |name, job|
-        build_job(name, job)
+    def jobs_for_stage_and_ref(stage, ref, tag = false, trigger_request = nil)
+      jobs_for_ref(ref, tag, trigger_request).select do |_, job|
+        job[:stage] == stage
       end
     end
 
-    def global_variables
-      @variables
+    def builds_for_ref(ref, tag = false, trigger_request = nil)
+      jobs_for_ref(ref, tag, trigger_request).map do |name, job|
+        build_job(name, job)
+      end
     end
 
-    def job_variables(name)
-      job = @jobs[name.to_sym]
-      return [] unless job
+    def builds_for_stage_and_ref(stage, ref, tag = false, trigger_request = nil)
+      jobs_for_stage_and_ref(stage, ref, tag, trigger_request).map do |name, job|
+        build_job(name, job)
+      end
+    end
 
-      job[:variables] || []
+    def builds
+      @jobs.map do |name, job|
+        build_job(name, job)
+      end
     end
 
     private
@@ -95,11 +101,10 @@ module Ci
         commands: [job[:before_script] || @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],
+        yaml_variables: yaml_variables(name),
         options: {
           image: job[:image] || @image,
           services: job[:services] || @services,
@@ -111,6 +116,24 @@ module Ci
       }
     end
 
+    def yaml_variables(name)
+      variables = global_variables.merge(job_variables(name))
+      variables.map do |key, value|
+        { key: key, value: value, public: true }
+      end
+    end
+
+    def global_variables
+      @variables || {}
+    end
+
+    def job_variables(name)
+      job = @jobs[name.to_sym]
+      return {} unless job
+
+      job[:variables] || {}
+    end
+
     def validate!
       @jobs.each do |name, job|
         validate_job!(name, job)
diff --git a/lib/container_registry/client.rb b/lib/container_registry/client.rb
index 42232b7129d2c4ab7f284d80d39b9f9d95aeebb4..2edddb84fc3f7b4d49324269f2d360dc4ee3fcd8 100644
--- a/lib/container_registry/client.rb
+++ b/lib/container_registry/client.rb
@@ -7,62 +7,91 @@ module ContainerRegistry
 
     MANIFEST_VERSION = 'application/vnd.docker.distribution.manifest.v2+json'
 
+    # Taken from: FaradayMiddleware::FollowRedirects
+    REDIRECT_CODES = Set.new [301, 302, 303, 307]
+
     def initialize(base_uri, options = {})
       @base_uri = base_uri
-      @faraday = Faraday.new(@base_uri) do |conn|
-        initialize_connection(conn, options)
-      end
+      @options = options
     end
 
     def repository_tags(name)
-      response_body @faraday.get("/v2/#{name}/tags/list")
+      response_body faraday.get("/v2/#{name}/tags/list")
     end
 
     def repository_manifest(name, reference)
-      response_body @faraday.get("/v2/#{name}/manifests/#{reference}")
+      response_body faraday.get("/v2/#{name}/manifests/#{reference}")
     end
 
     def repository_tag_digest(name, reference)
-      response = @faraday.head("/v2/#{name}/manifests/#{reference}")
+      response = faraday.head("/v2/#{name}/manifests/#{reference}")
       response.headers['docker-content-digest'] if response.success?
     end
 
     def delete_repository_tag(name, reference)
-      @faraday.delete("/v2/#{name}/manifests/#{reference}").success?
+      faraday.delete("/v2/#{name}/manifests/#{reference}").success?
     end
 
     def blob(name, digest, type = nil)
-      headers = {}
-      headers['Accept'] = type if type
-      response_body @faraday.get("/v2/#{name}/blobs/#{digest}", nil, headers)
+      type ||= 'application/octet-stream'
+      response_body faraday_blob.get("/v2/#{name}/blobs/#{digest}", nil, 'Accept' => type), allow_redirect: true
     end
 
     def delete_blob(name, digest)
-      @faraday.delete("/v2/#{name}/blobs/#{digest}").success?
+      faraday.delete("/v2/#{name}/blobs/#{digest}").success?
     end
-    
+
     private
-    
+
     def initialize_connection(conn, options)
       conn.request :json
+
+      if options[:user] && options[:password]
+        conn.request(:basic_auth, options[:user].to_s, options[:password].to_s)
+      elsif options[:token]
+        conn.request(:authorization, :bearer, options[:token].to_s)
+      end
+
+      conn.adapter :net_http
+    end
+
+    def accept_manifest(conn)
       conn.headers['Accept'] = MANIFEST_VERSION
 
       conn.response :json, content_type: 'application/json'
       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'
+    end
 
-      if options[:user] && options[:password]
-        conn.request(:basic_auth, options[:user].to_s, options[:password].to_s)
-      elsif options[:token]
-        conn.request(:authorization, :bearer, options[:token].to_s)
+    def response_body(response, allow_redirect: false)
+      if allow_redirect && REDIRECT_CODES.include?(response.status)
+        response = redirect_response(response.headers['location'])
       end
 
-      conn.adapter :net_http
+      response.body if response && response.success?
+    end
+
+    def redirect_response(location)
+      return unless location
+
+      # We explicitly remove authorization token
+      faraday_blob.get(location) do |req|
+        req['Authorization'] = ''
+      end
     end
 
-    def response_body(response)
-      response.body if response.success?
+    def faraday
+      @faraday ||= Faraday.new(@base_uri) do |conn|
+        initialize_connection(conn, @options)
+        accept_manifest(conn)
+      end
+    end
+
+    def faraday_blob
+      @faraday_blob ||= Faraday.new(@base_uri) do |conn|
+        initialize_connection(conn, @options)
+      end
     end
   end
 end
diff --git a/lib/container_registry/tag.rb b/lib/container_registry/tag.rb
index 708d01b95a1b14595cebb63580e3481e261e5d69..59040199920de94acbf180ba1771785120d620bf 100644
--- a/lib/container_registry/tag.rb
+++ b/lib/container_registry/tag.rb
@@ -53,7 +53,7 @@ module ContainerRegistry
     def config
       return unless config_blob
 
-      @config ||= ContainerRegistry::Config.new(self, config_blob)
+      @config ||= ContainerRegistry::Config.new(self, config_blob) if config_blob.data
     end
 
     def created_at
diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb
index 478f145bfedaf9782c3fccc433775e889d3528f0..ab94abeda7719396865170842a90219816319ed0 100644
--- a/lib/gitlab/backend/grack_auth.rb
+++ b/lib/gitlab/backend/grack_auth.rb
@@ -63,7 +63,7 @@ module Grack
     def ci_request?(login, password)
       matched_login = /(?<s>^[a-zA-Z]*-ci)-token$/.match(login)
 
-      if project && matched_login.present? && git_cmd == 'git-upload-pack'
+      if project && matched_login.present?
         underscored_service = matched_login['s'].underscore
 
         if underscored_service == 'gitlab_ci'
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index dec20d8659b1559a13abbae577db0b500d4a6b3d..927f9dad20bee1c16f1d0104c1a395f7dc4a3b5a 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -20,11 +20,19 @@ module Gitlab
 
         if Database.postgresql?
           options = options.merge({ algorithm: :concurrently })
+          disable_statement_timeout
         end
 
         add_index(table_name, column_name, options)
       end
 
+      # Long-running migrations may take more than the timeout allowed by
+      # the database. Disable the session's statement timeout to ensure
+      # migrations don't get killed prematurely. (PostgreSQL only)
+      def disable_statement_timeout
+        ActiveRecord::Base.connection.execute('SET statement_timeout TO 0') if Database.postgresql?
+      end
+
       # Updates the value of a column in batches.
       #
       # This method updates the table in batches of 5% of the total row count.
@@ -133,6 +141,8 @@ module Gitlab
             'in the body of your migration class'
         end
 
+        disable_statement_timeout
+
         transaction do
           add_column(table, column, type, default: nil)
 
diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb
index b0c50edba59642975157000039c07753361018ca..7e01f7b61fba1ff5f4e807dbe11ed77603eee3fd 100644
--- a/lib/gitlab/diff/file.rb
+++ b/lib/gitlab/diff/file.rb
@@ -68,6 +68,10 @@ module Gitlab
         @lines ||= Gitlab::Diff::Parser.new.parse(raw_diff.each_line).to_a
       end
 
+      def collapsed_by_default?
+        diff.diff.bytesize > 10240 # 10 KB
+      end
+
       def highlighted_diff_lines
         @highlighted_diff_lines ||= Gitlab::Diff::Highlight.new(self, repository: self.repository).highlight
       end
diff --git a/lib/gitlab/diff/inline_diff.rb b/lib/gitlab/diff/inline_diff.rb
index 789c14518b003260f302c86ac14928ec69c52cb2..28ad637fda455b646e3faab3ec207499e0ad0814 100644
--- a/lib/gitlab/diff/inline_diff.rb
+++ b/lib/gitlab/diff/inline_diff.rb
@@ -1,16 +1,30 @@
 module Gitlab
   module Diff
     class InlineDiff
+      # Regex to find a run of deleted lines followed by the same number of added lines
+      LINE_PAIRS_PATTERN = %r{
+        # Runs start at the beginning of the string (the first line) or after a space (for an unchanged line)
+        (?:\A|\s)
+
+        # This matches a number of `-`s followed by the same number of `+`s through recursion
+        (?<del_ins>
+          -
+          \g<del_ins>?
+          \+
+        )
+
+        # Runs end at the end of the string (the last line) or before a space (for an unchanged line)
+        (?=\s|\z)
+      }x.freeze
+
       attr_accessor :old_line, :new_line, :offset
 
       def self.for_lines(lines)
-        local_edit_indexes = self.find_local_edits(lines)
+        changed_line_pairs = self.find_changed_line_pairs(lines)
 
         inline_diffs = []
 
-        local_edit_indexes.each do |index|
-          old_index = index
-          new_index = index + 1
+        changed_line_pairs.each do |old_index, new_index|
           old_line = lines[old_index]
           new_line = lines[new_index]
 
@@ -51,18 +65,28 @@ module Gitlab
 
       private
 
-      def self.find_local_edits(lines)
-        line_prefixes = lines.map { |line| line.match(/\A([+-])/) ? $1 : ' ' }
-        joined_line_prefixes = " #{line_prefixes.join} "
-
-        offset = 0
-        local_edit_indexes = []
-        while index = joined_line_prefixes.index(" -+ ", offset)
-          local_edit_indexes << index
-          offset = index + 1
+      # Finds pairs of old/new line pairs that represent the same line that changed
+      def self.find_changed_line_pairs(lines)
+        # Prefixes of all diff lines, indicating their types
+        # For example: `" - +  -+  ---+++ --+  -++"`
+        line_prefixes = lines.each_with_object("") { |line, s| s << line[0] }.gsub(/[^ +-]/, ' ')
+
+        changed_line_pairs = []
+        line_prefixes.scan(LINE_PAIRS_PATTERN) do
+          # For `"---+++"`, `begin_index == 0`, `end_index == 6`
+          begin_index, end_index = Regexp.last_match.offset(:del_ins)
+
+          # For `"---+++"`, `changed_line_count == 3`
+          changed_line_count = (end_index - begin_index) / 2
+
+          halfway_index = begin_index + changed_line_count
+          (begin_index...halfway_index).each do |i|
+            # For `"---+++"`, index 1 maps to 1 + 3 = 4
+            changed_line_pairs << [i, i + changed_line_count]
+          end
         end
 
-        local_edit_indexes
+        changed_line_pairs
       end
 
       def longest_common_prefix(a, b)
diff --git a/lib/gitlab/diff/parallel_diff.rb b/lib/gitlab/diff/parallel_diff.rb
index 1c1fc148123ef436b0185b75a9f79ce443940a34..b069afdd28c1a03eb3083c326ee32393f78aa2bc 100644
--- a/lib/gitlab/diff/parallel_diff.rb
+++ b/lib/gitlab/diff/parallel_diff.rb
@@ -8,95 +8,78 @@ module Gitlab
       end
 
       def parallelize
-        lines = []
-        skip_next = false
 
+        i = 0
+        free_right_index = nil
+
+        lines = []
         highlighted_diff_lines = diff_file.highlighted_diff_lines
         highlighted_diff_lines.each do |line|
-          full_line = line.text
-          type = line.type
           line_code = diff_file.line_code(line)
-          line_new = line.new_pos
-          line_old = line.old_pos
           position = diff_file.position(line)
 
-          next_line = diff_file.next_line(line.index)
-
-          if next_line
-            next_line = highlighted_diff_lines[next_line.index]
-            full_next_line = next_line.text
-            next_line_code = diff_file.line_code(next_line)
-            next_type = next_line.type
-            next_position = diff_file.position(next_line)
-          end
-
-          case type
+          case line.type
           when 'match', nil
             # line in the right panel is the same as in the left one
             lines << {
               left: {
-                type:       type,
-                number:     line_old,
-                text:       full_line,
+                type:       line.type,
+                number:     line.old_pos,
+                text:       line.text,
                 line_code:  line_code,
                 position:   position
               },
               right: {
-                type:       type,
-                number:     line_new,
-                text:       full_line,
+                type:       line.type,
+                number:     line.new_pos,
+                text:       line.text,
                 line_code:  line_code,
                 position:   position
               }
             }
+
+            free_right_index = nil
+            i += 1
           when 'old'
-            case next_type
-            when 'new'
-              # Left side has text removed, right side has text added
-              lines << {
-                left: {
-                  type:       type,
-                  number:     line_old,
-                  text:       full_line,
-                  line_code:  line_code,
-                  position:   position
-                },
-                right: {
-                  type:       next_type,
-                  number:     line_new,
-                  text:       full_next_line,
-                  line_code:  next_line_code,
-                  position:   next_position,
-                }
-              }
-              skip_next = true
-            when 'old', 'nonewline', nil
-              # Left side has text removed, right side doesn't have any change
-              # No next line code, no new line number, no new line text
-              lines << {
-                left: {
-                  type:       type,
-                  number:     line_old,
-                  text:       full_line,
-                  line_code:  line_code,
-                  position:   position
-                },
-                right: {
-                  type:       next_type,
-                  number:     nil,
-                  text:       "",
-                  line_code:  nil,
-                  position:   nil
-                }
+            lines << {
+              left: {
+                type:       line.type,
+                number:     line.old_pos,
+                text:       line.text,
+                line_code:  line_code,
+                position:   position
+              },
+              right: {
+                type:       nil,
+                number:     nil,
+                text:       "",
+                line_code:  line_code,
+                position:   position
               }
-            end
+            }
+
+            # Once we come upon a new line it can be put on the right of this old line
+            free_right_index ||= i
+            i += 1
           when 'new'
-            if skip_next
-              # Change has been already included in previous line so no need to do it again
-              skip_next = false
-              next
+            data = {
+              type:       line.type,
+              number:     line.new_pos,
+              text:       line.text,
+              line_code:  line_code,
+              position:   position
+            }
+
+            if free_right_index
+              # If an old line came before this without a line on the right, this
+              # line can be put to the right of it.
+              lines[free_right_index][:right] = data
+
+              # If there are any other old lines on the left that don't yet have
+              # a new counterpart on the right, update the free_right_index
+              next_free_right_index = free_right_index + 1
+              free_right_index = next_free_right_index < i ? next_free_right_index : nil
             else
-              # Change is only on the right side, left side has no change
               lines << {
                 left: {
                   type:       nil,
@@ -105,17 +88,15 @@ module Gitlab
                   line_code:  line_code,
                   position:   position
                 },
-                right: {
-                  type:       type,
-                  number:     line_new,
-                  text:       full_line,
-                  line_code:  line_code,
-                  position:   position
-                }
+                right: data
               }
+
+              free_right_index = nil
+              i += 1
             end
           end
         end
+
         lines
       end
     end
diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb
index 043f10d96a953192f84668446005377c2d92fbf4..084e514492c2d4337b5154811cca10e36af3bbaf 100644
--- a/lib/gitlab/github_import/client.rb
+++ b/lib/gitlab/github_import/client.rb
@@ -78,10 +78,21 @@ module Gitlab
 
       def rate_limit
         api.rate_limit!
+      # GitHub Rate Limit API returns 404 when the rate limit is
+      # disabled. In this case we just want to return gracefully
+      # instead of spitting out an error.
+      rescue Octokit::NotFound
+        nil
+      end
+
+      def has_rate_limit?
+        return @has_rate_limit if defined?(@has_rate_limit)
+
+        @has_rate_limit = rate_limit.present?
       end
 
       def rate_limit_exceed?
-        rate_limit.remaining <= GITHUB_SAFE_REMAINING_REQUESTS
+        has_rate_limit? && rate_limit.remaining <= GITHUB_SAFE_REMAINING_REQUESTS
       end
 
       def rate_limit_sleep_time
diff --git a/lib/gitlab/gitlab_import/importer.rb b/lib/gitlab/gitlab_import/importer.rb
index 3f76ec979778e142e8cc6bc12a45e90a144c34f9..46d40f75be6b93fa503ec47e9b4253dd555c87ed 100644
--- a/lib/gitlab/gitlab_import/importer.rb
+++ b/lib/gitlab/gitlab_import/importer.rb
@@ -15,31 +15,35 @@ module Gitlab
       end
 
       def execute
-        project_identifier = CGI.escape(project.import_source)
-
-        # Issues && Comments
-        issues = client.issues(project_identifier)
-
-        issues.each do |issue|
-          body = @formatter.author_line(issue["author"]["name"])
-          body += issue["description"]
-
-          comments = client.issue_comments(project_identifier, issue["id"])
-
-          if comments.any?
-            body += @formatter.comments_header
+        ActiveRecord::Base.no_touching do
+          project_identifier = CGI.escape(project.import_source)
+
+          # Issues && Comments
+          issues = client.issues(project_identifier)
+
+          issues.each do |issue|
+            body = @formatter.author_line(issue["author"]["name"])
+            body += issue["description"]
+
+            comments = client.issue_comments(project_identifier, issue["id"])
+
+            if comments.any?
+              body += @formatter.comments_header
+            end
+
+            comments.each do |comment|
+              body += @formatter.comment(comment["author"]["name"], comment["created_at"], comment["body"])
+            end
+
+            project.issues.create!(
+              iid: issue["iid"],
+              description: body,
+              title: issue["title"],
+              state: issue["state"],
+              updated_at: issue["updated_at"],
+              author_id: gl_user_id(project, issue["author"]["id"])
+            )
           end
-
-          comments.each do |comment|
-            body += @formatter.comment(comment["author"]["name"], comment["created_at"], comment["body"])
-          end
-
-          project.issues.create!(
-            description: body,
-            title: issue["title"],
-            state: issue["state"],
-            author_id: gl_user_id(project, issue["author"]["id"])
-          )
         end
 
         true
diff --git a/lib/gitlab/highlight.rb b/lib/gitlab/highlight.rb
index 41296415e35700619c36d02ba5982d1c4018e0a5..9360afedfcb4b1df697f97482d8aede813a4dac5 100644
--- a/lib/gitlab/highlight.rb
+++ b/lib/gitlab/highlight.rb
@@ -1,7 +1,7 @@
 module Gitlab
   class Highlight
-    def self.highlight(blob_name, blob_content, repository: nil, nowrap: true, plain: false)
-      new(blob_name, blob_content, nowrap: nowrap, repository: repository).
+    def self.highlight(blob_name, blob_content, repository: nil, plain: false)
+      new(blob_name, blob_content, repository: repository).
         highlight(blob_content, continue: false, plain: plain)
     end
 
@@ -13,30 +13,34 @@ module Gitlab
       highlight(file_name, blob.data, repository: repository).lines.map!(&:html_safe)
     end
 
-    attr_reader :lexer
-    def initialize(blob_name, blob_content, repository: nil, nowrap: true)
+    def initialize(blob_name, blob_content, repository: nil)
+      @formatter = Rouge::Formatters::HTMLGitlab.new
+      @repository = repository
       @blob_name = blob_name
       @blob_content = blob_content
-      @repository = repository
-      @formatter = rouge_formatter(nowrap: nowrap)
-
-      @lexer = custom_language || begin
-        Rouge::Lexer.guess(filename: blob_name, source: blob_content).new
-      rescue Rouge::Lexer::AmbiguousGuess => e
-        e.alternatives.sort_by(&:tag).first
-      end
     end
 
     def highlight(text, continue: true, plain: false)
       if plain
-        @formatter.format(Rouge::Lexers::PlainText.lex(text)).html_safe
+        hl_lexer = Rouge::Lexers::PlainText
+        continue = false
       else
-        @formatter.format(@lexer.lex(text, continue: continue)).html_safe
+        hl_lexer = self.lexer
       end
+
+      @formatter.format(hl_lexer.lex(text, continue: continue)).html_safe
     rescue
       @formatter.format(Rouge::Lexers::PlainText.lex(text)).html_safe
     end
 
+    def lexer
+      @lexer ||= custom_language || begin
+        Rouge::Lexer.guess(filename: @blob_name, source: @blob_content).new
+      rescue Rouge::Guesser::Ambiguous => e
+        e.alternatives.sort_by(&:tag).first
+      end
+    end
+
     private
 
     def custom_language
@@ -46,16 +50,5 @@ module Gitlab
 
       Rouge::Lexer.find_fancy(language_name)
     end
-
-    def rouge_formatter(options = {})
-      options = options.reverse_merge(
-        nowrap: true,
-        cssclass: 'code highlight',
-        lineanchors: true,
-        lineanchorsid: 'LC'
-      )
-
-      Rouge::Formatters::HTMLGitlab.new(options)
-    end
   end
 end
diff --git a/lib/gitlab/import_export.rb b/lib/gitlab/import_export.rb
index 588647e5adb6e47859360958306d8f0fc08756da..bab2ea73c4f10a63636a81dabd66cb3eecddeb0a 100644
--- a/lib/gitlab/import_export.rb
+++ b/lib/gitlab/import_export.rb
@@ -3,6 +3,7 @@ module Gitlab
     extend self
 
     VERSION = '0.1.1'
+    FILENAME_LIMIT = 50
 
     def export_path(relative_path:)
       File.join(storage_path, relative_path)
@@ -28,6 +29,12 @@ module Gitlab
       'VERSION'
     end
 
+    def export_filename(project:)
+      basename = "#{Time.now.strftime('%Y-%m-%d_%H-%M-%3N')}_#{project.namespace.path}_#{project.path}"
+
+      "#{basename[0..FILENAME_LIMIT]}_export.tar.gz"
+    end
+
     def version
       VERSION
     end
diff --git a/lib/gitlab/import_export/importer.rb b/lib/gitlab/import_export/importer.rb
index 8f66f48cbfeff88fb39d6fb63b294d51e5e4ec52..6b69a653f12183b2d2ac7572b486f28a64606d8d 100644
--- a/lib/gitlab/import_export/importer.rb
+++ b/lib/gitlab/import_export/importer.rb
@@ -44,8 +44,7 @@ module Gitlab
       def wiki_restorer
         Gitlab::ImportExport::RepoRestorer.new(path_to_bundle: wiki_repo_path,
                                                shared: @shared,
-                                               project: ProjectWiki.new(project_tree.restored_project),
-                                               wiki: true)
+                                               project: ProjectWiki.new(project_tree.restored_project))
       end
 
       def uploads_restorer
diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb
index 0ac6ff01e3beb7dc4bdd7cf5cbbaf403c191ae95..051110c23cfddb9501675edf259cd27589ed9983 100644
--- a/lib/gitlab/import_export/project_tree_restorer.rb
+++ b/lib/gitlab/import_export/project_tree_restorer.rb
@@ -12,7 +12,10 @@ module Gitlab
         json = IO.read(@path)
         @tree_hash = ActiveSupport::JSON.decode(json)
         @project_members = @tree_hash.delete('project_members')
-        create_relations
+
+        ActiveRecord::Base.no_touching do
+          create_relations
+        end
       rescue => e
         @shared.error(e)
         false
@@ -69,10 +72,19 @@ module Gitlab
       # Example:
       # +relation_key+ issues, loops through the list of *issues* and for each individual
       # issue, finds any subrelations such as notes, creates them and assign them back to the hash
+      #
+      # Recursively calls this method if the sub-relation is a hash containing more sub-relations
       def create_sub_relations(relation, tree_hash)
         relation_key = relation.keys.first.to_s
+        return if tree_hash[relation_key].blank?
+
         tree_hash[relation_key].each do |relation_item|
           relation.values.flatten.each do |sub_relation|
+            # We just use author to get the user ID, do not attempt to create an instance.
+            next if sub_relation == :author
+
+            create_sub_relations(sub_relation, relation_item) if sub_relation.is_a?(Hash)
+
             relation_hash, sub_relation = assign_relation_hash(relation_item, sub_relation)
             relation_item[sub_relation.to_s] = create_relation(sub_relation, relation_hash) unless relation_hash.blank?
           end
diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb
index 9824df3f2748a0e80678886e6c60f50dfd821a59..6ba25a31641bd87b844b30f3102a334caa3f9560 100644
--- a/lib/gitlab/import_export/relation_factory.rb
+++ b/lib/gitlab/import_export/relation_factory.rb
@@ -87,7 +87,7 @@ module Gitlab
         project_id = @relation_hash.delete('project_id')
 
         # project_id may not be part of the export, but we always need to populate it if required.
-        @relation_hash['project_id'] = project_id if relation_class.column_names.include?('project_id')
+        @relation_hash['project_id'] = project_id
         @relation_hash['gl_project_id'] = project_id if @relation_hash['gl_project_id']
         @relation_hash['target_project_id'] = project_id if @relation_hash['target_project_id']
         @relation_hash['source_project_id'] = -1 if @relation_hash['source_project_id']
@@ -111,7 +111,7 @@ module Gitlab
       end
 
       def imported_object
-        imported_object = relation_class.new(@relation_hash)
+        imported_object = relation_class.new(parsed_relation_hash)
         yield(imported_object) if block_given?
         imported_object.importing = true if imported_object.respond_to?(:importing)
         imported_object
@@ -125,6 +125,10 @@ module Gitlab
       def admin_user?
         @user.is_admin?
       end
+
+      def parsed_relation_hash
+        @relation_hash.reject { |k, _v| !relation_class.attribute_method?(k) }
+      end
     end
   end
 end
diff --git a/lib/gitlab/import_export/repo_restorer.rb b/lib/gitlab/import_export/repo_restorer.rb
index 546dae4d122547d12ad29bed5858950694472cbd..f84de652a572e602c7cb89b642eb712fef8f2e9f 100644
--- a/lib/gitlab/import_export/repo_restorer.rb
+++ b/lib/gitlab/import_export/repo_restorer.rb
@@ -3,15 +3,14 @@ module Gitlab
     class RepoRestorer
       include Gitlab::ImportExport::CommandLineUtil
 
-      def initialize(project:, shared:, path_to_bundle:, wiki: false)
+      def initialize(project:, shared:, path_to_bundle:)
         @project = project
         @path_to_bundle = path_to_bundle
         @shared = shared
-        @wiki = wiki
       end
 
       def restore
-        return wiki? unless File.exist?(@path_to_bundle)
+        return true unless File.exist?(@path_to_bundle)
 
         FileUtils.mkdir_p(path_to_repo)
 
@@ -30,10 +29,6 @@ module Gitlab
       def path_to_repo
         @project.repository.path_to_repo
       end
-
-      def wiki?
-        @wiki
-      end
     end
   end
 end
diff --git a/lib/gitlab/import_export/repo_saver.rb b/lib/gitlab/import_export/repo_saver.rb
index cce43fe994bbd7b55d4b1d2b532caa3ce0fbb8a0..331e14021e6831ef388720ede3abcc99d65791a3 100644
--- a/lib/gitlab/import_export/repo_saver.rb
+++ b/lib/gitlab/import_export/repo_saver.rb
@@ -11,7 +11,7 @@ module Gitlab
       end
 
       def save
-        return false if @project.empty_repo?
+        return true if @project.empty_repo? # it's ok to have no repo
 
         @full_path = File.join(@shared.export_path, ImportExport.project_bundle_filename)
         bundle_to_disk
diff --git a/lib/gitlab/import_export/saver.rb b/lib/gitlab/import_export/saver.rb
index 6a60b65071f6116a7b671774d766e9971b5300aa..6130c124dd12ff31f5244c73d4c4087b1c33ece6 100644
--- a/lib/gitlab/import_export/saver.rb
+++ b/lib/gitlab/import_export/saver.rb
@@ -7,7 +7,8 @@ module Gitlab
         new(*args).save
       end
 
-      def initialize(shared:)
+      def initialize(project:, shared:)
+        @project = project
         @shared = shared
       end
 
@@ -36,7 +37,7 @@ module Gitlab
       end
 
       def archive_file
-        @archive_file ||= File.join(@shared.export_path, '..', "#{Time.now.strftime('%Y-%m-%d_%H-%M-%3N')}_project_export.tar.gz")
+        @archive_file ||= File.join(@shared.export_path, '..', Gitlab::ImportExport.export_filename(project: @project))
       end
     end
   end
diff --git a/lib/gitlab/import_export/wiki_repo_saver.rb b/lib/gitlab/import_export/wiki_repo_saver.rb
index 1eedae39f8a3a7017f46ceb0cc4149da17a899ac..6107420e4dd79ab6bb8b7d1d60b72e2111877e21 100644
--- a/lib/gitlab/import_export/wiki_repo_saver.rb
+++ b/lib/gitlab/import_export/wiki_repo_saver.rb
@@ -4,6 +4,7 @@ module Gitlab
       def save
         @wiki = ProjectWiki.new(@project)
         return true unless wiki_repository_exists? # it's okay to have no Wiki
+
         bundle_to_disk(File.join(@shared.export_path, project_filename))
       end
 
diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb
index 811363405a813110f1ee5df06e3b24a9a57ac5b3..a1ee1aa81ff090de51b6c1db73076ed5261a5dca 100644
--- a/lib/gitlab/lfs/response.rb
+++ b/lib/gitlab/lfs/response.rb
@@ -47,6 +47,8 @@ module Gitlab
       end
 
       def render_storage_upload_store_response(oid, size, tmp_file_name)
+        return render_forbidden unless tmp_file_name
+
         render_response_to_push do
           render_lfs_upload_ok(oid, size, tmp_file_name)
         end
diff --git a/lib/gitlab/lfs/router.rb b/lib/gitlab/lfs/router.rb
index 69bd5e6230587830b381fcf1a4adb378b09b2e61..f2a76a56b8f2452ea1a789e90feb20a548830060 100644
--- a/lib/gitlab/lfs/router.rb
+++ b/lib/gitlab/lfs/router.rb
@@ -74,8 +74,6 @@ module Gitlab
           lfs.render_storage_upload_authorize_response(oid, size)
         else
           tmp_file_name = sanitize_tmp_filename(@request.env['HTTP_X_GITLAB_LFS_TMP'])
-          return nil unless tmp_file_name
-
           lfs.render_storage_upload_store_response(oid, size, tmp_file_name)
         end
       end
diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb
index bc0193a6c3287fd112be35cafca93e1d51687aad..6aeb49c02196e5534f6159da131d56ea3431cab3 100644
--- a/lib/gitlab/workhorse.rb
+++ b/lib/gitlab/workhorse.rb
@@ -63,6 +63,18 @@ module Gitlab
         ]
       end
 
+      def send_artifacts_entry(build, entry)
+        params = {
+          'Archive' => build.artifacts_file.path,
+          'Entry' => Base64.encode64(entry.path)
+        }
+
+        [
+          SEND_DATA_HEADER,
+          "artifacts-entry:#{encode(params)}"
+        ]
+      end
+
       protected
 
       def encode(hash)
diff --git a/lib/rouge/formatters/html_gitlab.rb b/lib/rouge/formatters/html_gitlab.rb
index 3358ed6773e0ae6895659978107c4c7fd24a6992..f818dc78d34c7f3f326ed41de2cda3d5f8a6cb3b 100644
--- a/lib/rouge/formatters/html_gitlab.rb
+++ b/lib/rouge/formatters/html_gitlab.rb
@@ -1,171 +1,27 @@
-require 'cgi'
-
 module Rouge
   module Formatters
-    class HTMLGitlab < Rouge::Formatter
+    class HTMLGitlab < Rouge::Formatters::HTML
       tag 'html_gitlab'
 
       # Creates a new <tt>Rouge::Formatter::HTMLGitlab</tt> instance.
       #
-      # [+nowrap+]          If set to True, don't wrap the output at all, not
-      #                     even inside a <tt><pre></tt> tag (default: false).
-      # [+cssclass+]        CSS class for the wrapping <tt><div></tt> tag
-      #                     (default: 'highlight').
-      # [+linenos+]         If set to 'table', output line numbers as a table
-      #                     with two cells, one containing the line numbers,
-      #                     the other the whole code. This is copy paste friendly,
-      #                     but may cause alignment problems with some browsers
-      #                     or fonts. If set to 'inline', the line numbers will
-      #                     be integrated in the <tt><pre></tt> tag that contains
-      #                     the code (default: nil).
       # [+linenostart+]     The line number for the first line (default: 1).
-      # [+lineanchors+]     If set to true the formatter will wrap each output
-      #                     line in an anchor tag with a name of L-linenumber.
-      #                     This allows easy linking to certain lines
-      #                     (default: false).
-      # [+lineanchorsid+]   If lineanchors is true the name of the anchors can
-      #                     be changed with lineanchorsid to e.g. foo-linenumber
-      #                     (default: 'L').
-      # [+anchorlinenos+]   If set to true, will wrap line numbers in <tt><a></tt>
-      #                     tags. Used in combination with linenos and lineanchors
-      #                     (default: false).
-      # [+inline_theme+]    Inline CSS styles for the <pre> tag (default: false).
-      def initialize(
-          nowrap: false,
-          cssclass: 'highlight',
-          linenos: nil,
-          linenostart: 1,
-          lineanchors: false,
-          lineanchorsid: 'L',
-          anchorlinenos: false,
-          inline_theme: nil
-      )
-        @nowrap = nowrap
-        @cssclass = cssclass
-        @linenos = linenos
+      def initialize(linenostart: 1)
         @linenostart = linenostart
-        @lineanchors = lineanchors
-        @lineanchorsid = lineanchorsid
-        @anchorlinenos = anchorlinenos
-        @inline_theme = Theme.find(inline_theme).new if inline_theme.is_a?(String)
-      end
-
-      def render(tokens)
-        case @linenos
-        when 'table'
-          render_tableized(tokens)
-        when 'inline'
-          render_untableized(tokens)
-        else
-          render_untableized(tokens)
-        end
-      end
-
-      alias_method :format, :render
-
-      private
-
-      def render_untableized(tokens)
-        data = process_tokens(tokens)
-
-        html = ''
-        html << "<pre class=\"#{@cssclass}\"><code>" unless @nowrap
-        html << wrap_lines(data[:code])
-        html << "</code></pre>\n" unless @nowrap
-        html
+        @line_number = linenostart
       end
 
-      def render_tableized(tokens)
-        data = process_tokens(tokens)
-
-        html = ''
-        html << "<div class=\"#{@cssclass}\">" unless @nowrap
-        html << '<table><tbody>'
-        html << "<td class=\"linenos\"><pre>"
-        html << wrap_linenos(data[:numbers])
-        html << '</pre></td>'
-        html << "<td class=\"lines\"><pre><code>"
-        html << wrap_lines(data[:code])
-        html << '</code></pre></td>'
-        html << '</tbody></table>'
-        html << '</div>' unless @nowrap
-        html
-      end
-
-      def process_tokens(tokens)
-        rendered = []
-        current_line = ''
-
-        tokens.each do |tok, val|
-          # In the case of multi-line values (e.g. comments), we need to apply
-          # styling to each line since span elements are inline.
-          val.lines.each do |line|
-            stripped = line.chomp
-            current_line << span(tok, stripped)
-
-            if line.end_with?("\n")
-              rendered << current_line
-              current_line = ''
-            end
-          end
-        end
-
-        # Add leftover text
-        rendered << current_line if current_line.present?
-
-        num_lines = rendered.size
-        numbers = (@linenostart..num_lines + @linenostart - 1).to_a
-
-        { numbers: numbers, code: rendered }
-      end
-
-      def wrap_linenos(numbers)
-        if @anchorlinenos
-          numbers.map! do |number|
-            "<a href=\"##{@lineanchorsid}#{number}\">#{number}</a>"
-          end
-        end
-        numbers.join("\n")
-      end
-
-      def wrap_lines(lines)
-        if @lineanchors
-          lines = lines.each_with_index.map do |line, index|
-            number = index + @linenostart
-
-            if @linenos == 'inline'
-              "<a name=\"L#{number}\"></a>" \
-              "<span class=\"linenos\">#{number}</span>" \
-              "<span id=\"#{@lineanchorsid}#{number}\" class=\"line\">#{line}" \
-              '</span>'
-            else
-              "<span id=\"#{@lineanchorsid}#{number}\" class=\"line\">#{line}" \
-              '</span>'
-            end
-          end
-        elsif @linenos == 'inline'
-          lines = lines.each_with_index.map do |line, index|
-            number = index + @linenostart
-            "<span class=\"linenos\">#{number}</span>#{line}"
-          end
-        end
-
-        lines.join("\n")
-      end
+      def stream(tokens, &b)
+        is_first = true
+        token_lines(tokens) do |line|
+          yield "\n" unless is_first
+          is_first = false
 
-      def span(tok, val)
-        # http://stackoverflow.com/a/1600584/2587286
-        val = CGI.escapeHTML(val)
+          yield %(<span id="LC#{@line_number}" class="line">)
+          line.each { |token, value| yield span(token, value) }
+          yield %(</span>)
 
-        if tok.shortname.empty?
-          val
-        else
-          if @inline_theme
-            rules = @inline_theme.style_for(tok).rendered_rules
-            "<span style=\"#{rules.to_a.join(';')}\"#{val}</span>"
-          else
-            "<span class=\"#{tok.shortname}\">#{val}</span>"
-          end
+          @line_number += 1
         end
       end
     end
diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab
index d521de28e8a293a943f5bfa57f54e1cf6ed2f9f0..4a4892a2e07ac5a93f4dfcf0ae85614b9a52c3a8 100644
--- a/lib/support/nginx/gitlab
+++ b/lib/support/nginx/gitlab
@@ -49,7 +49,12 @@ server {
 
     proxy_http_version 1.1;
 
-    proxy_set_header    Host                $http_host;
+    ## By overwriting Host and clearing X-Forwarded-Host we ensure that
+    ## internal HTTP redirects generated by GitLab always send users to
+    ## YOUR_SERVER_FQDN.
+    proxy_set_header    Host                YOUR_SERVER_FQDN;
+    proxy_set_header    X-Forwarded-Host    "";
+
     proxy_set_header    X-Real-IP           $remote_addr;
     proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
     proxy_set_header    X-Forwarded-Proto   $scheme;
diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl
index bf014b56cf68062d3da68c1416aea7e7d09c83de..0b93d7f292f12b6f6303dd3ab06bc6083d9e3af4 100644
--- a/lib/support/nginx/gitlab-ssl
+++ b/lib/support/nginx/gitlab-ssl
@@ -93,7 +93,12 @@ server {
 
     proxy_http_version 1.1;
 
-    proxy_set_header    Host                $http_host;
+    ## By overwriting Host and clearing X-Forwarded-Host we ensure that
+    ## internal HTTP redirects generated by GitLab always send users to
+    ## YOUR_SERVER_FQDN.
+    proxy_set_header    Host                YOUR_SERVER_FQDN;
+    proxy_set_header    X-Forwarded-Host    "";
+
     proxy_set_header    X-Real-IP           $remote_addr;
     proxy_set_header    X-Forwarded-Ssl     on;
     proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
diff --git a/spec/controllers/commit_controller_spec.rb b/spec/controllers/commit_controller_spec.rb
deleted file mode 100644
index a3a3309e15ea3ca6556c71cc4ad85e34791b2eed..0000000000000000000000000000000000000000
--- a/spec/controllers/commit_controller_spec.rb
+++ /dev/null
@@ -1,246 +0,0 @@
-require 'spec_helper'
-
-describe Projects::CommitController do
-  let(:project) { create(:project) }
-  let(:user)    { create(:user) }
-  let(:commit)  { project.commit("master") }
-  let(:master_pickable_sha) { '7d3b0f7cff5f37573aea97cebfd5692ea1689924' }
-  let(:master_pickable_commit)  { project.commit(master_pickable_sha) }
-
-  before do
-    sign_in(user)
-    project.team << [user, :master]
-  end
-
-  describe "#show" do
-    shared_examples "export as" do |format|
-      it "should generally work" do
-        get(:show,
-            namespace_id: project.namespace.to_param,
-            project_id: project.to_param,
-            id: commit.id,
-            format: format)
-
-        expect(response).to be_success
-      end
-
-      it "should generate it" do
-        expect_any_instance_of(Commit).to receive(:"to_#{format}")
-
-        get(:show,
-            namespace_id: project.namespace.to_param,
-            project_id: project.to_param,
-            id: commit.id, format: format)
-      end
-
-      it "should render it" do
-        get(:show,
-            namespace_id: project.namespace.to_param,
-            project_id: project.to_param,
-            id: commit.id, format: format)
-
-        expect(response.body).to eq(commit.send(:"to_#{format}"))
-      end
-
-      it "should not escape Html" do
-        allow_any_instance_of(Commit).to receive(:"to_#{format}").
-          and_return('HTML entities &<>" ')
-
-        get(:show,
-            namespace_id: project.namespace.to_param,
-            project_id: project.to_param,
-            id: commit.id, format: format)
-
-        expect(response.body).not_to include('&amp;')
-        expect(response.body).not_to include('&gt;')
-        expect(response.body).not_to include('&lt;')
-        expect(response.body).not_to include('&quot;')
-      end
-    end
-
-    describe "as diff" do
-      include_examples "export as", :diff
-      let(:format) { :diff }
-
-      it "should really only be a git diff" do
-        get(:show,
-            namespace_id: project.namespace.to_param,
-            project_id: project.to_param,
-            id: commit.id,
-            format: format)
-
-        expect(response.body).to start_with("diff --git")
-      end
-      
-      it "should really only be a git diff without whitespace changes" do
-        get(:show,
-            namespace_id: project.namespace.to_param,
-            project_id: project.to_param,
-            id: '66eceea0db202bb39c4e445e8ca28689645366c5',
-            # id: commit.id,
-            format: format,
-            w: 1)
-
-        expect(response.body).to start_with("diff --git")
-        # without whitespace option, there are more than 2 diff_splits
-        diff_splits = assigns(:diffs).first.diff.split("\n")
-        expect(diff_splits.length).to be <= 2
-      end
-    end
-
-    describe "as patch" do
-      include_examples "export as", :patch
-      let(:format) { :patch }
-
-      it "should really be a git email patch" do
-        get(:show,
-            namespace_id: project.namespace.to_param,
-            project_id: project.to_param,
-            id: commit.id,
-            format: format)
-
-        expect(response.body).to start_with("From #{commit.id}")
-      end
-
-      it "should contain a git diff" do
-        get(:show,
-            namespace_id: project.namespace.to_param,
-            project_id: project.to_param,
-            id: commit.id,
-            format: format)
-
-        expect(response.body).to match(/^diff --git/)
-      end
-    end
-
-    context 'commit that removes a submodule' do
-      render_views
-
-      let(:fork_project) { create(:forked_project_with_submodules) }
-      let(:commit) { fork_project.commit('remove-submodule') }
-
-      before do
-        fork_project.team << [user, :master]
-      end
-
-      it 'renders it' do
-        get(:show,
-            namespace_id: fork_project.namespace.to_param,
-            project_id: fork_project.to_param,
-            id: commit.id)
-
-        expect(response).to be_success
-      end
-    end
-  end
-
-  describe "#branches" do
-    it "contains branch and tags information" do
-      get(:branches,
-          namespace_id: project.namespace.to_param,
-          project_id: project.to_param,
-          id: commit.id)
-
-      expect(assigns(:branches)).to include("master", "feature_conflict")
-      expect(assigns(:tags)).to include("v1.1.0")
-    end
-  end
-
-  describe '#revert' do
-    context 'when target branch is not provided' do
-      it 'should render the 404 page' do
-        post(:revert,
-            namespace_id: project.namespace.to_param,
-            project_id: project.to_param,
-            id: commit.id)
-
-        expect(response).not_to be_success
-        expect(response).to have_http_status(404)
-      end
-    end
-
-    context 'when the revert was successful' do
-      it 'should redirect to the commits page' do
-        post(:revert,
-            namespace_id: project.namespace.to_param,
-            project_id: project.to_param,
-            target_branch: 'master',
-            id: commit.id)
-
-        expect(response).to redirect_to namespace_project_commits_path(project.namespace, project, 'master')
-        expect(flash[:notice]).to eq('The commit has been successfully reverted.')
-      end
-    end
-
-    context 'when the revert failed' do
-      before do
-        post(:revert,
-            namespace_id: project.namespace.to_param,
-            project_id: project.to_param,
-            target_branch: 'master',
-            id: commit.id)
-      end
-
-      it 'should redirect to the commit page' do
-        # Reverting a commit that has been already reverted.
-        post(:revert,
-            namespace_id: project.namespace.to_param,
-            project_id: project.to_param,
-            target_branch: 'master',
-            id: commit.id)
-
-        expect(response).to redirect_to namespace_project_commit_path(project.namespace, project, commit.id)
-        expect(flash[:alert]).to match('Sorry, we cannot revert this commit automatically.')
-      end
-    end
-  end
-
-  describe '#cherry_pick' do
-    context 'when target branch is not provided' do
-      it 'should render the 404 page' do
-        post(:cherry_pick,
-            namespace_id: project.namespace.to_param,
-            project_id: project.to_param,
-            id: master_pickable_commit.id)
-
-        expect(response).not_to be_success
-        expect(response).to have_http_status(404)
-      end
-    end
-
-    context 'when the cherry-pick was successful' do
-      it 'should redirect to the commits page' do
-        post(:cherry_pick,
-            namespace_id: project.namespace.to_param,
-            project_id: project.to_param,
-            target_branch: 'master',
-            id: master_pickable_commit.id)
-
-        expect(response).to redirect_to namespace_project_commits_path(project.namespace, project, 'master')
-        expect(flash[:notice]).to eq('The commit has been successfully cherry-picked.')
-      end
-    end
-
-    context 'when the cherry_pick failed' do
-      before do
-        post(:cherry_pick,
-            namespace_id: project.namespace.to_param,
-            project_id: project.to_param,
-            target_branch: 'master',
-            id: master_pickable_commit.id)
-      end
-
-      it 'should redirect to the commit page' do
-        # Cherry-picking a commit that has been already cherry-picked.
-        post(:cherry_pick,
-            namespace_id: project.namespace.to_param,
-            project_id: project.to_param,
-            target_branch: 'master',
-            id: master_pickable_commit.id)
-
-        expect(response).to redirect_to namespace_project_commit_path(project.namespace, project, master_pickable_commit.id)
-        expect(flash[:alert]).to match('Sorry, we cannot cherry-pick this commit automatically.')
-      end
-    end
-  end
-end
diff --git a/spec/controllers/help_controller_spec.rb b/spec/controllers/help_controller_spec.rb
index 1f7fd517342b694f5a8689971982471122d0d008..267d511c2dbca06342b188b0c7fa2dd3533b8a38 100644
--- a/spec/controllers/help_controller_spec.rb
+++ b/spec/controllers/help_controller_spec.rb
@@ -11,7 +11,7 @@ describe HelpController do
     context 'for Markdown formats' do
       context 'when requested file exists' do
         before do
-          get :show, category: 'ssh', file: 'README', format: :md
+          get :show, path: 'ssh/README', format: :md
         end
 
         it 'assigns to @markdown' do
@@ -26,7 +26,7 @@ describe HelpController do
 
       context 'when requested file is missing' do
         it 'renders not found' do
-          get :show, category: 'foo', file: 'bar', format: :md
+          get :show, path: 'foo/bar', format: :md
           expect(response).to be_not_found
         end
       end
@@ -36,8 +36,7 @@ describe HelpController do
       context 'when requested file exists' do
         it 'renders the raw file' do
           get :show,
-              category: 'workflow/protected_branches',
-              file: 'protected_branches1',
+              path: 'workflow/protected_branches/protected_branches1',
               format: :png
           expect(response).to be_success
           expect(response.content_type).to eq 'image/png'
@@ -48,8 +47,7 @@ describe HelpController do
       context 'when requested file is missing' do
         it 'renders not found' do
           get :show,
-              category: 'foo',
-              file: 'bar',
+              path: 'foo/bar',
               format: :png
           expect(response).to be_not_found
         end
@@ -59,8 +57,7 @@ describe HelpController do
     context 'for other formats' do
       it 'always renders not found' do
         get :show,
-            category: 'ssh',
-            file: 'README',
+            path: 'ssh/README',
             format: :foo
         expect(response).to be_not_found
       end
diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb
index 6e3db10e451af4fd066fc977c40af2eaac9a703e..3001d32e719bd7efd7276f63e140437aec68e227 100644
--- a/spec/controllers/projects/commit_controller_spec.rb
+++ b/spec/controllers/projects/commit_controller_spec.rb
@@ -1,9 +1,29 @@
-require 'rails_helper'
+require 'spec_helper'
 
 describe Projects::CommitController do
+  let(:project) { create(:project) }
+  let(:user)    { create(:user) }
+  let(:commit)  { project.commit("master") }
+  let(:master_pickable_sha) { '7d3b0f7cff5f37573aea97cebfd5692ea1689924' }
+  let(:master_pickable_commit)  { project.commit(master_pickable_sha) }
+
+  before do
+    sign_in(user)
+    project.team << [user, :master]
+  end
+
   describe 'GET show' do
     render_views
 
+    def go(extra_params = {})
+      params = {
+        namespace_id: project.namespace.to_param,
+        project_id: project.to_param
+      }
+
+      get :show, params.merge(extra_params)
+    end
+
     let(:project) { create(:project) }
 
     before do
@@ -15,7 +35,7 @@ describe Projects::CommitController do
 
     context 'with valid id' do
       it 'responds with 200' do
-        go id: project.commit.id
+        go(id: commit.id)
 
         expect(response).to be_ok
       end
@@ -23,27 +43,274 @@ describe Projects::CommitController do
 
     context 'with invalid id' do
       it 'responds with 404' do
-        go id: project.commit.id.reverse
+        go(id: commit.id.reverse)
 
         expect(response).to be_not_found
       end
     end
 
     it 'handles binary files' do
-      get(:show,
+      go(id: TestEnv::BRANCH_SHA['binary-encoding'], format: 'html')
+
+      expect(response).to be_success
+    end
+
+    shared_examples "export as" do |format|
+      it "should generally work" do
+        go(id: commit.id, format: format)
+
+        expect(response).to be_success
+      end
+
+      it "should generate it" do
+        expect_any_instance_of(Commit).to receive(:"to_#{format}")
+
+        go(id: commit.id, format: format)
+      end
+
+      it "should render it" do
+        go(id: commit.id, format: format)
+
+        expect(response.body).to eq(commit.send(:"to_#{format}"))
+      end
+
+      it "should not escape Html" do
+        allow_any_instance_of(Commit).to receive(:"to_#{format}").
+          and_return('HTML entities &<>" ')
+
+        go(id: commit.id, format: format)
+
+        expect(response.body).not_to include('&amp;')
+        expect(response.body).not_to include('&gt;')
+        expect(response.body).not_to include('&lt;')
+        expect(response.body).not_to include('&quot;')
+      end
+    end
+
+    describe "as diff" do
+      include_examples "export as", :diff
+      let(:format) { :diff }
+
+      it "should really only be a git diff" do
+        go(id: commit.id, format: format)
+
+        expect(response.body).to start_with("diff --git")
+      end
+
+      it "should really only be a git diff without whitespace changes" do
+        go(id: '66eceea0db202bb39c4e445e8ca28689645366c5', format: format, w: 1)
+
+        expect(response.body).to start_with("diff --git")
+        # without whitespace option, there are more than 2 diff_splits
+        diff_splits = assigns(:diffs).first.diff.split("\n")
+        expect(diff_splits.length).to be <= 2
+      end
+    end
+
+    describe "as patch" do
+      include_examples "export as", :patch
+      let(:format) { :patch }
+
+      it "should really be a git email patch" do
+        go(id: commit.id, format: format)
+
+        expect(response.body).to start_with("From #{commit.id}")
+      end
+
+      it "should contain a git diff" do
+        go(id: commit.id, format: format)
+
+        expect(response.body).to match(/^diff --git/)
+      end
+    end
+
+    context 'commit that removes a submodule' do
+      render_views
+
+      let(:fork_project) { create(:forked_project_with_submodules, visibility_level: 20) }
+      let(:commit) { fork_project.commit('remove-submodule') }
+
+      it 'renders it' do
+        get(:show,
+            namespace_id: fork_project.namespace.to_param,
+            project_id: fork_project.to_param,
+            id: commit.id)
+
+        expect(response).to be_success
+      end
+    end
+  end
+
+  describe "GET branches" do
+    it "contains branch and tags information" do
+      get(:branches,
           namespace_id: project.namespace.to_param,
           project_id: project.to_param,
-          id: TestEnv::BRANCH_SHA['binary-encoding'],
-          format: "html")
+          id: commit.id)
 
-      expect(response).to be_success
+      expect(assigns(:branches)).to include("master", "feature_conflict")
+      expect(assigns(:tags)).to include("v1.1.0")
+    end
+  end
+
+  describe 'POST revert' do
+    context 'when target branch is not provided' do
+      it 'should render the 404 page' do
+        post(:revert,
+            namespace_id: project.namespace.to_param,
+            project_id: project.to_param,
+            id: commit.id)
+
+        expect(response).not_to be_success
+        expect(response).to have_http_status(404)
+      end
+    end
+
+    context 'when the revert was successful' do
+      it 'should redirect to the commits page' do
+        post(:revert,
+            namespace_id: project.namespace.to_param,
+            project_id: project.to_param,
+            target_branch: 'master',
+            id: commit.id)
+
+        expect(response).to redirect_to namespace_project_commits_path(project.namespace, project, 'master')
+        expect(flash[:notice]).to eq('The commit has been successfully reverted.')
+      end
+    end
+
+    context 'when the revert failed' do
+      before do
+        post(:revert,
+            namespace_id: project.namespace.to_param,
+            project_id: project.to_param,
+            target_branch: 'master',
+            id: commit.id)
+      end
+
+      it 'should redirect to the commit page' do
+        # Reverting a commit that has been already reverted.
+        post(:revert,
+            namespace_id: project.namespace.to_param,
+            project_id: project.to_param,
+            target_branch: 'master',
+            id: commit.id)
+
+        expect(response).to redirect_to namespace_project_commit_path(project.namespace, project, commit.id)
+        expect(flash[:alert]).to match('Sorry, we cannot revert this commit automatically.')
+      end
+    end
+  end
+
+  describe 'POST cherry_pick' do
+    context 'when target branch is not provided' do
+      it 'should render the 404 page' do
+        post(:cherry_pick,
+            namespace_id: project.namespace.to_param,
+            project_id: project.to_param,
+            id: master_pickable_commit.id)
+
+        expect(response).not_to be_success
+        expect(response).to have_http_status(404)
+      end
+    end
+
+    context 'when the cherry-pick was successful' do
+      it 'should redirect to the commits page' do
+        post(:cherry_pick,
+            namespace_id: project.namespace.to_param,
+            project_id: project.to_param,
+            target_branch: 'master',
+            id: master_pickable_commit.id)
+
+        expect(response).to redirect_to namespace_project_commits_path(project.namespace, project, 'master')
+        expect(flash[:notice]).to eq('The commit has been successfully cherry-picked.')
+      end
     end
 
-    def go(id:)
-      get :show,
+    context 'when the cherry_pick failed' do
+      before do
+        post(:cherry_pick,
+            namespace_id: project.namespace.to_param,
+            project_id: project.to_param,
+            target_branch: 'master',
+            id: master_pickable_commit.id)
+      end
+
+      it 'should redirect to the commit page' do
+        # Cherry-picking a commit that has been already cherry-picked.
+        post(:cherry_pick,
+            namespace_id: project.namespace.to_param,
+            project_id: project.to_param,
+            target_branch: 'master',
+            id: master_pickable_commit.id)
+
+        expect(response).to redirect_to namespace_project_commit_path(project.namespace, project, master_pickable_commit.id)
+        expect(flash[:alert]).to match('Sorry, we cannot cherry-pick this commit automatically.')
+      end
+    end
+  end
+
+  describe 'GET diff_for_path' do
+    def diff_for_path(extra_params = {})
+      params = {
         namespace_id: project.namespace.to_param,
-        project_id: project.to_param,
-        id: id
+        project_id: project.to_param
+      }
+
+      get :diff_for_path, params.merge(extra_params)
+    end
+
+    let(:existing_path) { '.gitmodules' }
+
+    context 'when the commit exists' do
+      context 'when the user has access to the project' do
+        context 'when the path exists in the diff' do
+          it 'enables diff notes' do
+            diff_for_path(id: commit.id, old_path: existing_path, new_path: existing_path)
+
+            expect(assigns(:diff_notes_disabled)).to be_falsey
+            expect(assigns(:comments_target)).to eq(noteable_type: 'Commit',
+                                                    commit_id: commit.id)
+          end
+
+          it 'only renders the diffs for the path given' do
+            expect(controller).to receive(:render_diff_for_path).and_wrap_original do |meth, diffs, diff_refs, project|
+              expect(diffs.map(&:new_path)).to contain_exactly(existing_path)
+              meth.call(diffs, diff_refs, project)
+            end
+
+            diff_for_path(id: commit.id, old_path: existing_path, new_path: existing_path)
+          end
+        end
+
+        context 'when the path does not exist in the diff' do
+          before { diff_for_path(id: commit.id, old_path: existing_path.succ, new_path: existing_path.succ) }
+
+          it 'returns a 404' do
+            expect(response).to have_http_status(404)
+          end
+        end
+      end
+
+      context 'when the user does not have access to the project' do
+        before do
+          project.team.truncate
+          diff_for_path(id: commit.id, old_path: existing_path, new_path: existing_path)
+        end
+
+        it 'returns a 404' do
+          expect(response).to have_http_status(404)
+        end
+      end
+    end
+
+    context 'when the commit does not exist' do
+      before { diff_for_path(id: commit.id.succ, old_path: existing_path, new_path: existing_path) }
+
+      it 'returns a 404' do
+        expect(response).to have_http_status(404)
+      end
     end
   end
 end
diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb
index 4018dac95a2e93b5f01d3c78ae58d1d038c99f5c..4058d5e2453cd207464698189f3c8ab73ebd3d79 100644
--- a/spec/controllers/projects/compare_controller_spec.rb
+++ b/spec/controllers/projects/compare_controller_spec.rb
@@ -64,4 +64,73 @@ describe Projects::CompareController do
       expect(assigns(:commits)).to eq(nil)
     end
   end
+
+  describe 'GET diff_for_path' do
+    def diff_for_path(extra_params = {})
+      params = {
+        namespace_id: project.namespace.to_param,
+        project_id: project.to_param
+      }
+
+      get :diff_for_path, params.merge(extra_params)
+    end
+
+    let(:existing_path) { 'files/ruby/feature.rb' }
+
+    context 'when the from and to refs exist' do
+      context 'when the user has access to the project' do
+        context 'when the path exists in the diff' do
+          it 'disables diff notes' do
+            diff_for_path(from: ref_from, to: ref_to, old_path: existing_path, new_path: existing_path)
+
+            expect(assigns(:diff_notes_disabled)).to be_truthy
+          end
+
+          it 'only renders the diffs for the path given' do
+            expect(controller).to receive(:render_diff_for_path).and_wrap_original do |meth, diffs, diff_refs, project|
+              expect(diffs.map(&:new_path)).to contain_exactly(existing_path)
+              meth.call(diffs, diff_refs, project)
+            end
+
+            diff_for_path(from: ref_from, to: ref_to, old_path: existing_path, new_path: existing_path)
+          end
+        end
+
+        context 'when the path does not exist in the diff' do
+          before { diff_for_path(from: ref_from, to: ref_to, old_path: existing_path.succ, new_path: existing_path.succ) }
+
+          it 'returns a 404' do
+            expect(response).to have_http_status(404)
+          end
+        end
+      end
+
+      context 'when the user does not have access to the project' do
+        before do
+          project.team.truncate
+          diff_for_path(from: ref_from, to: ref_to, old_path: existing_path, new_path: existing_path)
+        end
+
+        it 'returns a 404' do
+          expect(response).to have_http_status(404)
+        end
+      end
+    end
+
+    context 'when the from ref does not exist' do
+      before { diff_for_path(from: ref_from.succ, to: ref_to, old_path: existing_path, new_path: existing_path) }
+
+      it 'returns a 404' do
+        expect(response).to have_http_status(404)
+      end
+    end
+
+    context 'when the to ref does not exist' do
+      before { diff_for_path(from: ref_from, to: ref_to.succ, old_path: existing_path, new_path: existing_path) }
+
+      it 'returns a 404' do
+        expect(response).to have_http_status(404)
+      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 c4b57e77804c4595756376d6deb8c5e5d27be21c..210085e3b1a199e99fa647cfb9172847b4909198 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -10,7 +10,7 @@ describe Projects::MergeRequestsController do
     project.team << [user, :master]
   end
 
-  describe '#new' do
+  describe 'GET new' do
     context 'merge request that removes a submodule' do
       render_views
 
@@ -34,7 +34,7 @@ describe Projects::MergeRequestsController do
     end
   end
 
-  describe "#show" do
+  describe "GET show" do
     shared_examples "export merge as" do |format|
       it "should generally work" do
         get(:show,
@@ -108,7 +108,7 @@ describe Projects::MergeRequestsController do
     end
   end
 
-  describe 'GET #index' do
+  describe 'GET index' do
     def get_merge_requests
       get :index,
           namespace_id: project.namespace.to_param,
@@ -140,7 +140,7 @@ describe Projects::MergeRequestsController do
     end
   end
 
-  describe 'PUT #update' do
+  describe 'PUT update' do
     context 'there is no source project' do
       let(:project)       { create(:project) }
       let(:fork_project)  { create(:forked_project_with_submodules) }
@@ -168,7 +168,7 @@ describe Projects::MergeRequestsController do
     end
   end
 
-  describe 'POST #merge' do
+  describe 'POST merge' do
     let(:base_params) do
       {
         namespace_id: project.namespace.path,
@@ -266,7 +266,7 @@ describe Projects::MergeRequestsController do
     end
   end
 
-  describe "DELETE #destroy" do
+  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
 
@@ -290,96 +290,210 @@ describe Projects::MergeRequestsController do
   end
 
   describe 'GET diffs' do
-    def go(format: 'html')
-      get :diffs,
-          namespace_id: project.namespace.to_param,
-          project_id: project.to_param,
-          id: merge_request.iid,
-          format: format
+    def go(extra_params = {})
+      params = {
+        namespace_id: project.namespace.to_param,
+        project_id: project.to_param,
+        id: merge_request.iid
+      }
+
+      get :diffs, params.merge(extra_params)
     end
 
-    context 'as html' do
-      it 'renders the diff template' do
-        go
+    context 'with default params' do
+      context 'as html' do
+        before { go(format: 'html') }
 
-        expect(response).to render_template('diffs')
+        it 'renders the diff template' do
+          expect(response).to render_template('diffs')
+        end
       end
-    end
 
-    context 'as json' do
-      it 'renders the diffs template to a string' do
-        go format: 'json'
+      context 'as json' do
+        before { go(format: 'json') }
 
-        expect(response).to render_template('projects/merge_requests/show/_diffs')
-        expect(JSON.parse(response.body)).to have_key('html')
+        it 'renders the diffs template to a string' do
+          expect(response).to render_template('projects/merge_requests/show/_diffs')
+          expect(JSON.parse(response.body)).to have_key('html')
+        end
       end
-    end
-
-    context 'with forked projects with submodules' do
-      render_views
 
-      let(:project) { create(:project) }
-      let(:fork_project) { create(:forked_project_with_submodules) }
-      let(:merge_request) { create(:merge_request_with_diffs, source_project: fork_project, source_branch: 'add-submodule-version-bump', target_branch: 'master', target_project: project) }
+      context 'with forked projects with submodules' do
+        render_views
 
-      before do
-        fork_project.build_forked_project_link(forked_to_project_id: fork_project.id, forked_from_project_id: project.id)
-        fork_project.save
-        merge_request.reload
-      end
+        let(:project) { create(:project) }
+        let(:fork_project) { create(:forked_project_with_submodules) }
+        let(:merge_request) { create(:merge_request_with_diffs, source_project: fork_project, source_branch: 'add-submodule-version-bump', target_branch: 'master', target_project: project) }
 
-      it 'renders' do
-        go format: 'json'
+        before do
+          fork_project.build_forked_project_link(forked_to_project_id: fork_project.id, forked_from_project_id: project.id)
+          fork_project.save
+          merge_request.reload
+          go(format: 'json')
+        end
 
-        expect(response).to be_success
-        expect(response.body).to have_content('Subproject commit')
+        it 'renders' do
+          expect(response).to be_success
+          expect(response.body).to have_content('Subproject commit')
+        end
       end
     end
-  end
 
-  describe 'GET diffs with ignore_whitespace_change' do
-    def go(format: 'html')
-      get :diffs,
-          namespace_id: project.namespace.to_param,
-          project_id: project.to_param,
-          id: merge_request.iid,
-          format: format,
-          w: 1
-    end
+    context 'with ignore_whitespace_change' do
+      context 'as html' do
+        before { go(format: 'html', w: 1) }
 
-    context 'as html' do
-      it 'renders the diff template' do
-        go
+        it 'renders the diff template' do
+          expect(response).to render_template('diffs')
+        end
+      end
+
+      context 'as json' do
+        before { go(format: 'json', w: 1) }
 
-        expect(response).to render_template('diffs')
+        it 'renders the diffs template to a string' do
+          expect(response).to render_template('projects/merge_requests/show/_diffs')
+          expect(JSON.parse(response.body)).to have_key('html')
+        end
       end
     end
 
-    context 'as json' do
-      it 'renders the diffs template to a string' do
-        go format: 'json'
+    context 'with view' do
+      before { go(view: 'parallel') }
 
-        expect(response).to render_template('projects/merge_requests/show/_diffs')
-        expect(JSON.parse(response.body)).to have_key('html')
+      it 'saves the preferred diff view in a cookie' do
+        expect(response.cookies['diff_view']).to eq('parallel')
       end
     end
   end
 
-  describe 'GET diffs with view' do
-    def go(extra_params = {})
+  describe 'GET diff_for_path' do
+    def diff_for_path(extra_params = {})
       params = {
         namespace_id: project.namespace.to_param,
-        project_id:   project.to_param,
-        id:           merge_request.iid
+        project_id: project.to_param
       }
 
-      get :diffs, params.merge(extra_params)
+      get :diff_for_path, params.merge(extra_params)
     end
 
-    it 'saves the preferred diff view in a cookie' do
-      go view: 'parallel'
+    context 'when an ID param is passed' do
+      let(:existing_path) { 'files/ruby/popen.rb' }
 
-      expect(response.cookies['diff_view']).to eq('parallel')
+      context 'when the merge request exists' do
+        context 'when the user can view the merge request' do
+          context 'when the path exists in the diff' do
+            it 'enables diff notes' do
+              diff_for_path(id: merge_request.iid, old_path: existing_path, new_path: existing_path)
+
+              expect(assigns(:diff_notes_disabled)).to be_falsey
+              expect(assigns(:comments_target)).to eq(noteable_type: 'MergeRequest',
+                                                      noteable_id: merge_request.id)
+            end
+
+            it 'only renders the diffs for the path given' do
+              expect(controller).to receive(:render_diff_for_path).and_wrap_original do |meth, diffs, diff_refs, project|
+                expect(diffs.map(&:new_path)).to contain_exactly(existing_path)
+                meth.call(diffs, diff_refs, project)
+              end
+
+              diff_for_path(id: merge_request.iid, old_path: existing_path, new_path: existing_path)
+            end
+          end
+
+          context 'when the path does not exist in the diff' do
+            before { diff_for_path(id: merge_request.iid, old_path: 'files/ruby/nopen.rb', new_path: 'files/ruby/nopen.rb') }
+
+            it 'returns a 404' do
+              expect(response).to have_http_status(404)
+            end
+          end
+        end
+
+        context 'when the user cannot view the merge request' do
+          before do
+            project.team.truncate
+            diff_for_path(id: merge_request.iid, old_path: existing_path, new_path: existing_path)
+          end
+
+          it 'returns a 404' do
+            expect(response).to have_http_status(404)
+          end
+        end
+      end
+
+      context 'when the merge request does not exist' do
+        before { diff_for_path(id: merge_request.iid.succ, old_path: existing_path, new_path: existing_path) }
+
+        it 'returns a 404' do
+          expect(response).to have_http_status(404)
+        end
+      end
+
+      context 'when the merge request belongs to a different project' do
+        let(:other_project) { create(:empty_project) }
+
+        before do
+          other_project.team << [user, :master]
+          diff_for_path(id: merge_request.iid, old_path: existing_path, new_path: existing_path, project_id: other_project.to_param)
+        end
+
+        it 'returns a 404' do
+          expect(response).to have_http_status(404)
+        end
+      end
+    end
+
+    context 'when source and target params are passed' do
+      let(:existing_path) { 'files/ruby/feature.rb' }
+
+      context 'when both branches are in the same project' do
+        it 'disables diff notes' do
+          diff_for_path(old_path: existing_path, new_path: existing_path, merge_request: { source_branch: 'feature', target_branch: 'master' })
+
+          expect(assigns(:diff_notes_disabled)).to be_truthy
+        end
+
+        it 'only renders the diffs for the path given' do
+          expect(controller).to receive(:render_diff_for_path).and_wrap_original do |meth, diffs, diff_refs, project|
+            expect(diffs.map(&:new_path)).to contain_exactly(existing_path)
+            meth.call(diffs, diff_refs, project)
+          end
+
+          diff_for_path(old_path: existing_path, new_path: existing_path, merge_request: { source_branch: 'feature', target_branch: 'master' })
+        end
+      end
+
+      context 'when the source branch is in a different project to the target' do
+        let(:other_project) { create(:project) }
+
+        before { other_project.team << [user, :master] }
+
+        context 'when the path exists in the diff' do
+          it 'disables diff notes' do
+            diff_for_path(old_path: existing_path, new_path: existing_path, merge_request: { source_project: other_project, source_branch: 'feature', target_branch: 'master' })
+
+            expect(assigns(:diff_notes_disabled)).to be_truthy
+          end
+
+          it 'only renders the diffs for the path given' do
+            expect(controller).to receive(:render_diff_for_path).and_wrap_original do |meth, diffs, diff_refs, project|
+              expect(diffs.map(&:new_path)).to contain_exactly(existing_path)
+              meth.call(diffs, diff_refs, project)
+            end
+
+            diff_for_path(old_path: existing_path, new_path: existing_path, merge_request: { source_project: other_project, source_branch: 'feature', target_branch: 'master' })
+          end
+        end
+
+        context 'when the path does not exist in the diff' do
+          before { diff_for_path(old_path: 'files/ruby/nopen.rb', new_path: 'files/ruby/nopen.rb', merge_request: { source_project: other_project, source_branch: 'feature', target_branch: 'master' }) }
+
+          it 'returns a 404' do
+            expect(response).to have_http_status(404)
+          end
+        end
+      end
     end
   end
 
diff --git a/spec/controllers/projects/todo_controller_spec.rb b/spec/controllers/projects/todo_controller_spec.rb
index 5a8bba285942ce7215b694e8b9e76e0edacbe8ed..936320a37090d756d789f88c8bce9de589bddce4 100644
--- a/spec/controllers/projects/todo_controller_spec.rb
+++ b/spec/controllers/projects/todo_controller_spec.rb
@@ -1,6 +1,8 @@
 require('spec_helper')
 
 describe Projects::TodosController do
+  include ApiHelpers
+
   let(:user)          { create(:user) }
   let(:project)       { create(:project) }
   let(:issue)         { create(:issue, project: project) }
@@ -8,43 +10,51 @@ describe Projects::TodosController do
 
   context 'Issues' do
     describe 'POST create' do
+      def go
+        post :create,
+          namespace_id: project.namespace.path,
+          project_id: project.path,
+          issuable_id: issue.id,
+          issuable_type: 'issue',
+          format: 'html'
+      end
+
       context 'when authorized' do
         before do
           sign_in(user)
           project.team << [user, :developer]
         end
 
-        it 'should create todo for issue' do
+        it 'creates todo for issue' do
           expect do
-            post(:create, namespace_id: project.namespace.path,
-                          project_id: project.path,
-                          issuable_id: issue.id,
-                          issuable_type: 'issue')
+            go
           end.to change { user.todos.count }.by(1)
 
           expect(response).to have_http_status(200)
         end
+
+        it 'returns todo path and pending count' do
+          go
+
+          expect(response).to have_http_status(200)
+          expect(json_response['count']).to eq 1
+          expect(json_response['delete_path']).to match(/\/dashboard\/todos\/\d{1}/)
+        end
       end
 
       context 'when not authorized' do
-        it 'should not create todo for issue that user has no access to' do
+        it 'does not create todo for issue that user has no access to' do
           sign_in(user)
           expect do
-            post(:create, namespace_id: project.namespace.path,
-                          project_id: project.path,
-                          issuable_id: issue.id,
-                          issuable_type: 'issue')
+            go
           end.to change { user.todos.count }.by(0)
 
           expect(response).to have_http_status(404)
         end
 
-        it 'should not create todo for issue when user not logged in' do
+        it 'does not create todo for issue when user not logged in' do
           expect do
-            post(:create, namespace_id: project.namespace.path,
-                          project_id: project.path,
-                          issuable_id: issue.id,
-                          issuable_type: 'issue')
+            go
           end.to change { user.todos.count }.by(0)
 
           expect(response).to have_http_status(302)
@@ -55,43 +65,51 @@ describe Projects::TodosController do
 
   context 'Merge Requests' do
     describe 'POST create' do
+      def go
+        post :create,
+          namespace_id: project.namespace.path,
+          project_id: project.path,
+          issuable_id: merge_request.id,
+          issuable_type: 'merge_request',
+          format: 'html'
+      end
+
       context 'when authorized' do
         before do
           sign_in(user)
           project.team << [user, :developer]
         end
 
-        it 'should create todo for merge request' do
+        it 'creates todo for merge request' do
           expect do
-            post(:create, namespace_id: project.namespace.path,
-                          project_id: project.path,
-                          issuable_id: merge_request.id,
-                          issuable_type: 'merge_request')
+            go
           end.to change { user.todos.count }.by(1)
 
           expect(response).to have_http_status(200)
         end
+
+        it 'returns todo path and pending count' do
+          go
+
+          expect(response).to have_http_status(200)
+          expect(json_response['count']).to eq 1
+          expect(json_response['delete_path']).to match(/\/dashboard\/todos\/\d{1}/)
+        end
       end
 
       context 'when not authorized' do
-        it 'should not create todo for merge request user has no access to' do
+        it 'does not create todo for merge request user has no access to' do
           sign_in(user)
           expect do
-            post(:create, namespace_id: project.namespace.path,
-                          project_id: project.path,
-                          issuable_id: merge_request.id,
-                          issuable_type: 'merge_request')
+            go
           end.to change { user.todos.count }.by(0)
 
           expect(response).to have_http_status(404)
         end
 
-        it 'should not create todo for merge request user has no access to' do
+        it 'does not create todo for merge request user has no access to' do
           expect do
-            post(:create, namespace_id: project.namespace.path,
-                          project_id: project.path,
-                          issuable_id: merge_request.id,
-                          issuable_type: 'merge_request')
+            go
           end.to change { user.todos.count }.by(0)
 
           expect(response).to have_http_status(302)
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index fe05a0cfc001977e51100f58a15f6c238dd462cc..5fb671df570cc4f02c35c0d4ec97981ece3b2a40 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -15,6 +15,11 @@ FactoryGirl.define do
         services: ["postgres"]
       }
     end
+    yaml_variables do
+      [
+        { key: :DB_NAME, value: 'postgres', public: true }
+      ]
+    end
 
     pipeline factory: :ci_pipeline
 
diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb
index 7fc20cd55557336e832f2ae115e98bce35b96a45..866e663f0262ea629387b692a05b94b2072ca54b 100644
--- a/spec/factories/todos.rb
+++ b/spec/factories/todos.rb
@@ -23,6 +23,10 @@ FactoryGirl.define do
       action { Todo::BUILD_FAILED }
     end
 
+    trait :approval_required do
+      action { Todo::APPROVAL_REQUIRED }
+    end
+
     trait :done do
       state :done
     end
diff --git a/spec/features/admin/admin_builds_spec.rb b/spec/features/admin/admin_builds_spec.rb
index a6198389f04357f24d7f89d47352b6f78ad5e49a..e177059d9597c2d790869dfda97c9a9abd371662 100644
--- a/spec/features/admin/admin_builds_spec.rb
+++ b/spec/features/admin/admin_builds_spec.rb
@@ -36,12 +36,45 @@ describe 'Admin Builds' do
       end
     end
 
+    context 'Pending tab' do
+      context 'when have pending builds' do
+        it 'shows pending builds' do
+          build1 = create(:ci_build, pipeline: pipeline, status: :pending)
+          build2 = create(:ci_build, pipeline: pipeline, status: :running)
+          build3 = create(:ci_build, pipeline: pipeline, status: :success)
+          build4 = create(:ci_build, pipeline: pipeline, status: :failed)
+
+          visit admin_builds_path(scope: :pending)
+
+          expect(page).to have_selector('.nav-links li.active', text: 'Pending')
+          expect(page.find('.build-link')).to have_content(build1.id)
+          expect(page.find('.build-link')).not_to have_content(build2.id)
+          expect(page.find('.build-link')).not_to have_content(build3.id)
+          expect(page.find('.build-link')).not_to have_content(build4.id)
+          expect(page).to have_link 'Cancel all'
+        end
+      end
+
+      context 'when have no builds pending' do
+        it 'shows a message' do
+          create(:ci_build, pipeline: pipeline, status: :success)
+
+          visit admin_builds_path(scope: :pending)
+
+          expect(page).to have_selector('.nav-links li.active', text: 'Pending')
+          expect(page).to have_content 'No builds to show'
+          expect(page).not_to have_link 'Cancel all'
+        end
+      end
+    end
+
     context 'Running tab' do
       context 'when have running builds' do
         it 'shows running builds' do
-          build1 = create(:ci_build, pipeline: pipeline, status: :pending)
+          build1 = create(:ci_build, pipeline: pipeline, status: :running)
           build2 = create(:ci_build, pipeline: pipeline, status: :success)
           build3 = create(:ci_build, pipeline: pipeline, status: :failed)
+          build4 = create(:ci_build, pipeline: pipeline, status: :pending)
 
           visit admin_builds_path(scope: :running)
 
@@ -49,6 +82,7 @@ describe 'Admin Builds' do
           expect(page.find('.build-link')).to have_content(build1.id)
           expect(page.find('.build-link')).not_to have_content(build2.id)
           expect(page.find('.build-link')).not_to have_content(build3.id)
+          expect(page.find('.build-link')).not_to have_content(build4.id)
           expect(page).to have_link 'Cancel all'
         end
       end
diff --git a/spec/features/compare_spec.rb b/spec/features/compare_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c62556948e0faadd495b976288131ca1cf89264e
--- /dev/null
+++ b/spec/features/compare_spec.rb
@@ -0,0 +1,42 @@
+require "spec_helper"
+
+describe "Compare", js: true do
+  let(:user)    { create(:user) }
+  let(:project) { create(:project) }
+
+  before do
+    project.team << [user, :master]
+    login_as user
+    visit namespace_project_compare_index_path(project.namespace, project, from: "master", to: "master")
+  end
+
+  describe "branches" do
+    it "should pre-populate fields" do
+      expect(page.find_field("from").value).to eq("master")
+    end
+
+    it "should compare branches" do
+      fill_in "from", with: "fea"
+      find("#from").click
+
+      click_link "feature"
+      expect(page.find_field("from").value).to eq("feature")
+
+      click_button "Compare"
+      expect(page).to have_content "Commits"
+    end
+  end
+
+  describe "tags" do
+    it "should compare tags" do
+      fill_in "from", with: "v1.0"
+      find("#from").click
+
+      click_link "v1.0.0"
+      expect(page.find_field("from").value).to eq("v1.0.0")
+
+      click_button "Compare"
+      expect(page).to have_content "Commits"
+    end
+  end
+end
diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..78bc888f2a6e3fa1ab2d255ead1f10bca603885c
--- /dev/null
+++ b/spec/features/expand_collapse_diffs_spec.rb
@@ -0,0 +1,207 @@
+require 'spec_helper'
+
+feature 'Expand and collapse diffs', js: true, feature: true do
+  include WaitForAjax
+
+  before do
+    login_as :admin
+    project = create(:project)
+    branch = 'expand-collapse-diffs'
+
+    # Ensure that undiffable.md is in .gitattributes
+    project.repository.copy_gitattributes(branch)
+    visit namespace_project_commit_path(project.namespace, project, project.commit(branch))
+    execute_script('window.ajaxUris = []; $(document).ajaxSend(function(event, xhr, settings) { ajaxUris.push(settings.url) });')
+  end
+
+  def file_container(filename)
+    find("[data-blob-diff-path*='#{filename}']")
+  end
+
+  # Use define_method instead of let (which is memoized) so that this just works across a
+  # reload.
+  #
+  files = [
+    'small_diff.md', 'large_diff.md', 'large_diff_renamed.md', 'undiffable.md',
+    'too_large.md', 'too_large_image.jpg'
+  ]
+
+  files.each do |file|
+    define_method(file.split('.').first) { file_container(file) }
+  end
+
+  context 'visiting a commit with collapsed diffs' do
+    it 'shows small diffs immediately' do
+      expect(small_diff).to have_selector('.code')
+      expect(small_diff).not_to have_selector('.nothing-here-block')
+    end
+
+    it 'collapses large diffs by default' do
+      expect(large_diff).not_to have_selector('.code')
+      expect(large_diff).to have_selector('.nothing-here-block')
+    end
+
+    it 'collapses large diffs for renamed files by default' do
+      expect(large_diff_renamed).not_to have_selector('.code')
+      expect(large_diff_renamed).to have_selector('.nothing-here-block')
+      expect(large_diff_renamed).to have_selector('.file-title .deletion')
+      expect(large_diff_renamed).to have_selector('.file-title .addition')
+    end
+
+    it 'shows non-renderable diffs as such immediately, regardless of their size' do
+      expect(undiffable).not_to have_selector('.code')
+      expect(undiffable).to have_selector('.nothing-here-block')
+      expect(undiffable).to have_content('gitattributes')
+    end
+
+    it 'does not allow diffs that are larger than the maximum size to be expanded' do
+      expect(too_large).not_to have_selector('.code')
+      expect(too_large).to have_selector('.nothing-here-block')
+      expect(too_large).to have_content('too large')
+    end
+
+    it 'shows image diffs immediately, regardless of their size' do
+      expect(too_large_image).not_to have_selector('.nothing-here-block')
+      expect(too_large_image).to have_selector('.image')
+    end
+
+    context 'expanding a diff for a renamed file' do
+      before do
+        large_diff_renamed.find('.nothing-here-block').click
+        wait_for_ajax
+      end
+
+      it 'shows the old content' do
+        old_line = large_diff_renamed.find('.line_content.old')
+
+        expect(old_line).to have_content('two copies')
+      end
+
+      it 'shows the new content' do
+        new_line = large_diff_renamed.find('.line_content.new', match: :prefer_exact)
+
+        expect(new_line).to have_content('three copies')
+      end
+    end
+
+    context 'expanding a large diff' do
+      before do
+        click_link('large_diff.md')
+        wait_for_ajax
+      end
+
+      it 'makes a request to get the content' do
+        ajax_uris = evaluate_script('ajaxUris')
+
+        expect(ajax_uris).not_to be_empty
+        expect(ajax_uris.first).to include('large_diff.md')
+      end
+
+      it 'shows the diff content' do
+        expect(large_diff).to have_selector('.code')
+        expect(large_diff).not_to have_selector('.nothing-here-block')
+      end
+
+      context 'adding a comment to the expanded diff' do
+        let(:comment_text) { 'A comment' }
+
+        before do
+          large_diff.find('.diff-line-num', match: :prefer_exact).hover
+          large_diff.find('.add-diff-note').click
+          large_diff.find('.note-textarea').send_keys comment_text
+          large_diff.find_button('Comment').click
+          wait_for_ajax
+        end
+
+        it 'adds the comment' do
+          expect(large_diff.find('.notes')).to have_content comment_text
+        end
+
+        context 'reloading the page' do
+          before { refresh }
+
+          it 'collapses the large diff by default' do
+            expect(large_diff).not_to have_selector('.code')
+            expect(large_diff).to have_selector('.nothing-here-block')
+          end
+
+          context 'expanding the diff' do
+            before do
+              click_link('large_diff.md')
+              wait_for_ajax
+            end
+
+            it 'shows the diff content' do
+              expect(large_diff).to have_selector('.code')
+              expect(large_diff).not_to have_selector('.nothing-here-block')
+            end
+
+            it 'shows the diff comment' do
+              expect(large_diff.find('.notes')).to have_content comment_text
+            end
+          end
+        end
+      end
+    end
+
+    context 'collapsing an expanded diff' do
+      before { click_link('small_diff.md') }
+
+      it 'hides the diff content' do
+        expect(small_diff).not_to have_selector('.code')
+        expect(small_diff).to have_selector('.nothing-here-block')
+      end
+
+      context 're-expanding the same diff' do
+        before { click_link('small_diff.md') }
+
+        it 'shows the diff content' do
+          expect(small_diff).to have_selector('.code')
+          expect(small_diff).not_to have_selector('.nothing-here-block')
+        end
+
+        it 'does not make a new HTTP request' do
+          expect(evaluate_script('ajaxUris')).not_to include(a_string_matching('small_diff.md'))
+        end
+      end
+    end
+  end
+
+  context 'expanding all diffs' do
+    before do
+      click_link('Expand all')
+      wait_for_ajax
+      execute_script('window.ajaxUris = []; $(document).ajaxSend(function(event, xhr, settings) { ajaxUris.push(settings.url) });')
+    end
+
+    it 'reloads the page with all diffs expanded' do
+      expect(small_diff).to have_selector('.code')
+      expect(small_diff).not_to have_selector('.nothing-here-block')
+
+      expect(large_diff).to have_selector('.code')
+      expect(large_diff).not_to have_selector('.nothing-here-block')
+    end
+
+    context 'collapsing an expanded diff' do
+      before { click_link('small_diff.md') }
+
+      it 'hides the diff content' do
+        expect(small_diff).not_to have_selector('.code')
+        expect(small_diff).to have_selector('.nothing-here-block')
+      end
+
+      context 're-expanding the same diff' do
+        before { click_link('small_diff.md') }
+
+        it 'shows the diff content' do
+          expect(small_diff).to have_selector('.code')
+          expect(small_diff).not_to have_selector('.nothing-here-block')
+        end
+
+        it 'does not make a new HTTP request' do
+          expect(evaluate_script('ajaxUris')).not_to include(a_string_matching('small_diff.md'))
+        end
+      end
+    end
+  end
+end
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index 891df65216d1d46649c798bdcab848a74a8f32da..2d8b59472e8733abf76c9c08f1b4323c587c750b 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -1,14 +1,26 @@
 require 'spec_helper'
 
 feature 'Group', feature: true do
+  before do
+    login_as(:admin)
+  end
+
+  describe 'creating a group with space in group path' do
+    it 'renders new group form with validation errors' do
+      visit new_group_path
+      fill_in 'Group path', with: 'space group'
+
+      click_button 'Create group'
+
+      expect(current_path).to eq(groups_path)
+      expect(page).to have_content("Path can contain only letters, digits, '_', '-' and '.'. Cannot start with '-' or end in '.'.")
+    end
+  end
+
   describe 'description' do
     let(:group) { create(:group) }
     let(:path)  { group_path(group) }
 
-    before do
-      login_as(:admin)
-    end
-
     it 'parses Markdown' do
       group.update_attribute(:description, 'This is **my** group')
       visit path
diff --git a/spec/features/help_pages_spec.rb b/spec/features/help_pages_spec.rb
index 8c6b669ce78283393c053393d32e2ad83a7cab23..1e2306d7f5930b9f7c67ce0094195141b25878e6 100644
--- a/spec/features/help_pages_spec.rb
+++ b/spec/features/help_pages_spec.rb
@@ -6,7 +6,7 @@ describe 'Help Pages', feature: true do
       login_as :user
     end
     it 'replace the variable $your_email with the email of the user' do
-      visit help_page_path('ssh', 'README')
+      visit help_page_path('ssh/README')
       expect(page).to have_content("ssh-keygen -t rsa -C \"#{@user.email}\"")
     end
   end
diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb
index 72b5ff231f7cde9a6a0877d8d313c6d227d5e1ec..58753ff21f6de84538bec0af72f0a4dbc1888eeb 100644
--- a/spec/features/login_spec.rb
+++ b/spec/features/login_spec.rb
@@ -28,6 +28,11 @@ feature 'Login', feature: true do
   end
 
   describe 'with two-factor authentication' do
+    def enter_code(code)
+      fill_in 'Two-Factor Authentication code', with: code
+      click_button 'Verify code'
+    end
+
     context 'with valid username/password' do
       let(:user) { create(:user, :two_factor) }
 
@@ -36,11 +41,6 @@ feature 'Login', feature: true do
         expect(page).to have_content('Two-Factor Authentication')
       end
 
-      def enter_code(code)
-        fill_in 'Two-Factor Authentication code', with: code
-        click_button 'Verify code'
-      end
-
       it 'does not show a "You are already signed in." error message' do
         enter_code(user.current_otp)
         expect(page).not_to have_content('You are already signed in.')
@@ -108,6 +108,39 @@ feature 'Login', feature: true do
         end
       end
     end
+
+    context 'logging in via OAuth' do
+      def saml_config
+        OpenStruct.new(name: 'saml', label: 'saml', args: {
+          assertion_consumer_service_url: 'https://localhost:3443/users/auth/saml/callback',
+          idp_cert_fingerprint: '26:43:2C:47:AF:F0:6B:D0:07:9C:AD:A3:74:FE:5D:94:5F:4E:9E:52',
+          idp_sso_target_url: 'https://idp.example.com/sso/saml',
+          issuer: 'https://localhost:3443/',
+          name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'
+        })
+      end
+
+      def stub_omniauth_config(messages)
+        Rails.application.env_config['devise.mapping'] = Devise.mappings[:user]
+        Rails.application.routes.disable_clear_and_finalize = true
+        Rails.application.routes.draw do
+          post '/users/auth/saml' => 'omniauth_callbacks#saml'
+        end
+        allow(Gitlab::OAuth::Provider).to receive_messages(providers: [:saml], config_for: saml_config)
+        allow(Gitlab.config.omniauth).to receive_messages(messages)
+        allow_any_instance_of(Object).to receive(:user_omniauth_authorize_path).with('saml').and_return('/users/auth/saml')
+      end
+
+      it 'should show 2FA prompt after OAuth login' do
+        stub_omniauth_config(enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'], providers: [saml_config])
+        user = create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: 'saml')
+        login_via('saml', user, 'my-uid')
+
+        expect(page).to have_content('Two-Factor Authentication')
+        enter_code(user.current_otp)
+        expect(current_path).to eq root_path
+      end
+    end
   end
 
   describe 'without two-factor authentication' do
diff --git a/spec/features/merge_requests/diffs_spec.rb b/spec/features/merge_requests/diffs_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c9a0059645d99f2010d226e7b154993e5271c0c8
--- /dev/null
+++ b/spec/features/merge_requests/diffs_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+feature 'Diffs URL', js: true, feature: true do
+  before do
+    login_as :admin
+    @merge_request = create(:merge_request)
+    @project = @merge_request.source_project
+  end
+
+  context 'when visit with */* as accept header' do
+    before(:each) do
+      page.driver.add_header('Accept', '*/*')
+    end
+
+    it 'renders the notes' do
+      create :note_on_merge_request, project: @project, noteable: @merge_request, note: 'Rebasing with master'
+
+      visit diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
+
+      # Load notes and diff through AJAX
+      expect(page).to have_css('.note-text', visible: false, text: 'Rebasing with master')
+      expect(page).to have_css('.diffs.tab-pane.active')
+    end
+  end
+end
diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb
index 5174168713cae7b97ce93d9d2943a1d6ca923d59..0b38c413f4401ce5fa4e5275e10575e4f9f3fdad 100644
--- a/spec/features/notes_on_merge_requests_spec.rb
+++ b/spec/features/notes_on_merge_requests_spec.rb
@@ -135,6 +135,28 @@ describe 'Comments', feature: true do
     end
   end
 
+  describe 'Handles cross-project system notes', js: true, feature: true do
+    let(:user) { create(:user) }
+    let(:project) { create(:project, :public) }
+    let(:project2) { create(:project, :private) }
+    let(:issue) { create(:issue, project: project2) }
+    let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'markdown') }
+    let!(:note) { create(:note_on_merge_request, :system, noteable: merge_request, project: project, note: "mentioned in #{issue.to_reference(project)}") }
+
+    it 'shows the system note' do
+      login_as :admin
+      visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+
+      expect(page).to have_css('.system-note')
+    end
+
+    it 'hides redacted system note' do
+      visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+
+      expect(page).not_to have_css('.system-note')
+    end
+  end
+
   describe 'On a merge request diff', js: true, feature: true do
     let(:merge_request) { create(:merge_request) }
     let(:project) { merge_request.source_project }
@@ -231,6 +253,7 @@ describe 'Comments', feature: true do
   end
 
   def click_diff_line(data = line_code)
-    execute_script("$('button[data-line-code=\"#{data}\"]').click()")
+    find(".line_holder[id='#{data}'] td.line_content").hover
+    find(".line_holder[id='#{data}'] button").trigger('click')
   end
 end
diff --git a/spec/features/projects/builds_spec.rb b/spec/features/projects/builds_spec.rb
index 16832c297acd6c14d7d53d0406c3d29af2d3219c..cab3dc1d167c8672259767e2512d185a4dcff864 100644
--- a/spec/features/projects/builds_spec.rb
+++ b/spec/features/projects/builds_spec.rb
@@ -13,17 +13,33 @@ describe "Builds" do
   end
 
   describe "GET /:project/builds" do
+    context "Pending scope" do
+      before do
+        visit namespace_project_builds_path(@project.namespace, @project, scope: :pending)
+      end
+
+      it "shows Pending tab builds" do
+        expect(page).to have_link 'Cancel running'
+        expect(page).to have_selector('.nav-links li.active', text: 'Pending')
+        expect(page).to have_content @build.short_sha
+        expect(page).to have_content @build.ref
+        expect(page).to have_content @build.name
+      end
+    end
+
     context "Running scope" do
       before do
         @build.run!
         visit namespace_project_builds_path(@project.namespace, @project, scope: :running)
       end
 
-      it { expect(page).to have_selector('.nav-links li.active', text: 'Running') }
-      it { expect(page).to have_link 'Cancel running' }
-      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 "shows Running tab builds" do
+        expect(page).to have_selector('.nav-links li.active', text: 'Running')
+        expect(page).to have_link 'Cancel running'
+        expect(page).to have_content @build.short_sha
+        expect(page).to have_content @build.ref
+        expect(page).to have_content @build.name
+      end
     end
 
     context "Finished scope" do
@@ -32,9 +48,11 @@ describe "Builds" do
         visit namespace_project_builds_path(@project.namespace, @project, scope: :finished)
       end
 
-      it { expect(page).to have_selector('.nav-links li.active', text: 'Finished') }
-      it { expect(page).to have_content 'No builds to show' }
-      it { expect(page).to have_link 'Cancel running' }
+      it "shows Finished tab builds" do
+        expect(page).to have_selector('.nav-links li.active', text: 'Finished')
+        expect(page).to have_content 'No builds to show'
+        expect(page).to have_link 'Cancel running'
+      end
     end
 
     context "All builds" do
@@ -43,11 +61,13 @@ describe "Builds" do
         visit namespace_project_builds_path(@project.namespace, @project)
       end
 
-      it { expect(page).to have_selector('.nav-links li.active', text: 'All') }
-      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).not_to have_link 'Cancel running' }
+      it "shows All tab builds" do
+        expect(page).to have_selector('.nav-links li.active', text: 'All')
+        expect(page).to have_content @build.short_sha
+        expect(page).to have_content @build.ref
+        expect(page).to have_content @build.name
+        expect(page).not_to have_link 'Cancel running'
+      end
     end
   end
 
diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb
index 6a39c302f550b90ef16a1bb38e79777109ae8abc..98ba93b4036b3ed0d45780f904deb99868ae41d7 100644
--- a/spec/features/projects/labels/update_prioritization_spec.rb
+++ b/spec/features/projects/labels/update_prioritization_spec.rb
@@ -76,7 +76,7 @@ feature 'Prioritize labels', feature: true do
         expect(page.all('li').last).to have_content('bug')
       end
 
-      visit current_url
+      refresh
       wait_for_ajax
 
       page.within('.prioritized-labels') do
diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb
index 13d980a326f512f9656c16e6a1dc3e0e808b9e2f..b6acc509342af993f534b124a8faf6f065a42b93 100644
--- a/spec/features/security/project/internal_access_spec.rb
+++ b/spec/features/security/project/internal_access_spec.rb
@@ -426,4 +426,23 @@ describe "Internal Project Access", feature: true  do
     it { is_expected.to be_denied_for :external }
     it { is_expected.to be_denied_for :visitor }
   end
+
+  describe "GET /:project_path/container_registry" do
+    before do
+      stub_container_registry_tags('latest')
+      stub_container_registry_config(enabled: true)
+    end
+
+    subject { namespace_project_container_registry_index_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_allowed_for guest }
+    it { is_expected.to be_allowed_for :user }
+    it { is_expected.to be_denied_for :external }
+    it { is_expected.to be_denied_for :visitor }
+  end
 end
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index ac9690cc12796f7001a98cfe7efd2df49229018f..ccb5c06dab013144d0d6974d892a9ecb40357954 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -362,4 +362,23 @@ describe "Private Project Access", feature: true  do
     it { is_expected.to be_denied_for :external }
     it { is_expected.to be_denied_for :visitor }
   end
+
+  describe "GET /:project_path/container_registry" do
+    before do
+      stub_container_registry_tags('latest')
+      stub_container_registry_config(enabled: true)
+    end
+
+    subject { namespace_project_container_registry_index_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
 end
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index 737897de52b694b006a7043b5053be28206569ec..985663e7c989a4c9d76bd9bdab53c99ebb2819a2 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -426,4 +426,23 @@ describe "Public Project Access", feature: true  do
     it { is_expected.to be_denied_for :external }
     it { is_expected.to be_denied_for :visitor }
   end
+
+  describe "GET /:project_path/container_registry" do
+    before do
+      stub_container_registry_tags('latest')
+      stub_container_registry_config(enabled: true)
+    end
+
+    subject { namespace_project_container_registry_index_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_allowed_for guest }
+    it { is_expected.to be_allowed_for :user }
+    it { is_expected.to be_allowed_for :external }
+    it { is_expected.to be_allowed_for :visitor }
+  end
 end
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
index 14613754f746c649e66eee8811bd787bcbf4687d..9335f5bf120b7c5362560873ed9021b9440d63f8 100644
--- a/spec/features/u2f_spec.rb
+++ b/spec/features/u2f_spec.rb
@@ -1,6 +1,8 @@
 require 'spec_helper'
 
 feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: true, js: true do
+  before { allow_any_instance_of(U2fHelper).to receive(:inject_u2f_api?).and_return(true) }
+
   def register_u2f_device(u2f_device = nil)
     u2f_device ||= FakeU2fDevice.new(page)
     u2f_device.respond_to_u2f_registration
@@ -208,21 +210,52 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
         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) }
+    describe "when more than one device has been registered by the same user" do
+      it "allows logging in with either device" do
+        # Register first device
+        user = login_as(:user)
+        user.update_attribute(:otp_required_for_login, true)
+        visit profile_two_factor_auth_path
+        expect(page).to have_content("Your U2F device needs to be set up.")
+        first_device = register_u2f_device
+
+        # Register second device
+        visit profile_two_factor_auth_path
+        expect(page).to have_content("Your U2F device needs to be set up.")
+        second_device = register_u2f_device
+        logout
+
+        # Authenticate as both devices
+        [first_device, second_device].each do |device|
+          login_as(user)
+          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"
 
-    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
+          expect(page.body).to match('Signed in successfully')
+
+          logout
+        end
+      end
     end
 
-    it "deletes u2f registrations" do
-      expect { click_on "Disable" }.to change { U2fRegistration.count }.from(1).to(0)
+    describe "when two-factor authentication is disabled" do
+      let(:user) { create(:user) }
+
+      before do
+        user = login_as(:user)
+        user.update_attribute(:otp_required_for_login, true)
+        visit profile_account_path
+        click_on 'Manage Two-Factor Authentication'
+        expect(page).to have_content("Your U2F device needs to be set up.")
+        register_u2f_device
+      end
+
+      it "deletes u2f registrations" do
+        expect { click_on "Disable" }.to change { U2fRegistration.count }.by(-1)
+      end
     end
   end
 end
diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb
index 1bd354815e47a7fc4b27d1fb3e23d332edbfe1d6..8db897b16466bfe3f61d622f50f108a8ac7a0e38 100644
--- a/spec/finders/notes_finder_spec.rb
+++ b/spec/finders/notes_finder_spec.rb
@@ -11,7 +11,7 @@ describe NotesFinder do
     project.team << [user, :master]
   end
 
-  describe :execute do
+  describe '#execute' do
     let(:params)  { { target_id: commit.id, target_type: 'commit', last_fetched_at: 1.hour.ago.to_i } }
 
     before do
diff --git a/spec/fixtures/parallel_diff_result.yml b/spec/fixtures/parallel_diff_result.yml
index 7d01183e3efed7febd06bc1ddc34f5a113eaa416..37066c8e9302bed55e518a58d9d79105b94137af 100644
--- a/spec/fixtures/parallel_diff_result.yml
+++ b/spec/fixtures/parallel_diff_result.yml
@@ -121,7 +121,7 @@
     :type: old
     :number: 9
     :text: |
-      -<span id="LC9" class="line">      <span class="k">raise</span> <span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span>
+      -<span id="LC9" class="line">      <span class="k">raise</span> <span class="s2">"System commands must be given as an array of strings"</span></span>
     :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9
     :position: !ruby/object:Gitlab::Diff::Position
       attributes:
@@ -136,7 +136,7 @@
     :type: new
     :number: 9
     :text: |
-      +<span id="LC9" class="line">      <span class="k">raise</span> <span class="no"><span class='idiff left'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff right'> </span><span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span>
+      +<span id="LC9" class="line">      <span class="k">raise</span> <span class="no"><span class='idiff left'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff right'> </span><span class="s2">"System commands must be given as an array of strings"</span></span>
     :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9
     :position: !ruby/object:Gitlab::Diff::Position
       attributes:
@@ -241,7 +241,7 @@
     :type: old
     :number: 13
     :text: |
-      -<span id="LC13" class="line">    <span class="n">vars</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">&quot;PWD&quot;</span> <span class="o">=&gt;</span> <span class="n">path</span> <span class="p">}</span></span>
+      -<span id="LC13" class="line">    <span class="n">vars</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"PWD"</span> <span class="o">=&gt;</span> <span class="n">path</span> <span class="p">}</span></span>
     :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_13_13
     :position: !ruby/object:Gitlab::Diff::Position
       attributes:
@@ -252,27 +252,6 @@
         :base_sha: 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9
         :start_sha: 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9
         :head_sha: 570e7b2abdd848b95f2f578043fc23bd6f6fd24d
-  :right:
-    :type: old
-    :number:
-    :text: ''
-    :line_code:
-    :position:
-- :left:
-    :type: old
-    :number: 14
-    :text: |
-      -<span id="LC14" class="line">    <span class="n">options</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">chdir: </span><span class="n">path</span> <span class="p">}</span></span>
-    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_14_13
-    :position: !ruby/object:Gitlab::Diff::Position
-      attributes:
-        :old_path: files/ruby/popen.rb
-        :new_path: files/ruby/popen.rb
-        :old_line: 14
-        :new_line:
-        :base_sha: 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9
-        :start_sha: 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9
-        :head_sha: 570e7b2abdd848b95f2f578043fc23bd6f6fd24d
   :right:
     :type: new
     :number: 13
@@ -289,16 +268,17 @@
         :start_sha: 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9
         :head_sha: 570e7b2abdd848b95f2f578043fc23bd6f6fd24d
 - :left:
-    :type:
-    :number:
-    :text: ''
-    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_14
+    :type: old
+    :number: 14
+    :text: |
+      -<span id="LC14" class="line">    <span class="n">options</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">chdir: </span><span class="n">path</span> <span class="p">}</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_14_13
     :position: !ruby/object:Gitlab::Diff::Position
       attributes:
         :old_path: files/ruby/popen.rb
         :new_path: files/ruby/popen.rb
-        :old_line:
-        :new_line: 14
+        :old_line: 14
+        :new_line:
         :base_sha: 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9
         :start_sha: 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9
         :head_sha: 570e7b2abdd848b95f2f578043fc23bd6f6fd24d
@@ -335,7 +315,7 @@
     :type: new
     :number: 15
     :text: |
-      +<span id="LC15" class="line">      <span class="s2">&quot;PWD&quot;</span> <span class="o">=&gt;</span> <span class="n">path</span></span>
+      +<span id="LC15" class="line">      <span class="s2">"PWD"</span> <span class="o">=&gt;</span> <span class="n">path</span></span>
     :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15
     :position: !ruby/object:Gitlab::Diff::Position
       attributes:
@@ -643,7 +623,7 @@
     :type:
     :number: 20
     :text: |2
-       <span id="LC26" class="line">    <span class="vi">@cmd_output</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span></span>
+       <span id="LC26" class="line">    <span class="vi">@cmd_output</span> <span class="o">=</span> <span class="s2">""</span></span>
     :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26
     :position: !ruby/object:Gitlab::Diff::Position
       attributes:
@@ -658,7 +638,7 @@
     :type:
     :number: 26
     :text: |2
-       <span id="LC26" class="line">    <span class="vi">@cmd_output</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span></span>
+       <span id="LC26" class="line">    <span class="vi">@cmd_output</span> <span class="o">=</span> <span class="s2">""</span></span>
     :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26
     :position: !ruby/object:Gitlab::Diff::Position
       attributes:
diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb
index 6d1c02db297926e9f6d15158e08787a107f0ddef..bd0108f99380c74e56d4960ea6f72cd45a6f2c58 100644
--- a/spec/helpers/blob_helper_spec.rb
+++ b/spec/helpers/blob_helper_spec.rb
@@ -16,19 +16,19 @@ describe BlobHelper do
 
   describe '#highlight' do
     it 'should return plaintext for unknown lexer context' do
-      result = helper.highlight(blob_name, no_context_content, nowrap: true)
-      expect(result).to eq('<span id="LC1" class="line">:type &quot;assem&quot;))</span>')
+      result = helper.highlight(blob_name, no_context_content)
+      expect(result).to eq(%[<pre class="code highlight"><code><span id="LC1" class="line">:type "assem"))</span></code></pre>])
     end
 
     it 'should highlight single block' do
-      expected = %Q[<span id="LC1" class="line"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span>
-<span id="LC2" class="line"><span class="ss">:type</span> <span class="s">&quot;assem&quot;</span><span class="p">))</span></span>]
+      expected = %Q[<pre class="code highlight"><code><span id="LC1" class="line"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span>
+<span id="LC2" class="line"><span class="ss">:type</span> <span class="s">"assem"</span><span class="p">))</span></span></code></pre>]
 
-      expect(helper.highlight(blob_name, blob_content, nowrap: true)).to eq(expected)
+      expect(helper.highlight(blob_name, blob_content)).to eq(expected)
     end
 
     it 'should highlight multi-line comments' do
-      result = helper.highlight(blob_name, multiline_content, nowrap: true)
+      result = helper.highlight(blob_name, multiline_content)
       html = Nokogiri::HTML(result)
       lines = html.search('.s')
       expect(lines.count).to eq(3)
@@ -41,33 +41,19 @@ describe BlobHelper do
       let(:blob_name) { 'test.diff' }
       let(:blob_content) { "+aaa\n+bbb\n- ccc\n ddd\n"}
       let(:expected) do
-        %q(<span id="LC1" class="line"><span class="gi">+aaa</span></span>
+        %q(<pre class="code highlight"><code><span id="LC1" class="line"><span class="gi">+aaa</span></span>
 <span id="LC2" class="line"><span class="gi">+bbb</span></span>
 <span id="LC3" class="line"><span class="gd">- ccc</span></span>
-<span id="LC4" class="line"> ddd</span>)
+<span id="LC4" class="line"> ddd</span></code></pre>)
       end
 
       it 'should highlight each line properly' do
-        result = helper.highlight(blob_name, blob_content, nowrap: true)
+        result = helper.highlight(blob_name, blob_content)
         expect(result).to eq(expected)
       end
     end
   end
 
-  describe "#highlighter" do
-    it 'should highlight continued blocks' do
-      # Both lines have LC1 as ID since formatter doesn't support continue at the moment
-      expected = [
-        '<span id="LC1" class="line"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span>',
-        '<span id="LC1" class="line"><span class="ss">:type</span> <span class="s">&quot;assem&quot;</span><span class="p">))</span></span>'
-      ]
-
-      highlighter = helper.highlighter(blob_name, blob_content, nowrap: true)
-      result = split_content.map{ |content| highlighter.highlight(content) }
-      expect(result).to eq(expected)
-    end
-  end
-
   describe "#sanitize_svg" do
     let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') }
     let(:data) { open(input_svg_path).read }
diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb
index e2db33d8345339f9b584620f1a9875103c368e0b..4b134a4841008a7c62dbd989bc566dd0c7cef2ad 100644
--- a/spec/helpers/diff_helper_spec.rb
+++ b/spec/helpers/diff_helper_spec.rb
@@ -31,26 +31,11 @@ describe DiffHelper do
     end
   end
 
-  describe 'diff_hard_limit_enabled?' do
-    it 'should return true if param is provided' do
-      allow(controller).to receive(:params) { { force_show_diff: true } }
-      expect(diff_hard_limit_enabled?).to be_truthy
-    end
-
-    it 'should return false if param is not provided' do
-      expect(diff_hard_limit_enabled?).to be_falsey
-    end
-  end
-
   describe 'diff_options' do
-    it 'should return hard limit for a diff if force diff is true' do
+    it 'should return hard limit for a diff' do
       allow(controller).to receive(:params) { { force_show_diff: true } }
       expect(diff_options).to include(Commit.max_diff_options)
     end
-
-    it 'should return safe limit for a diff if force diff is false' do
-      expect(diff_options).not_to include(:max_lines, :max_files)
-    end
   end
 
   describe 'unfold_bottom_class' do
@@ -59,7 +44,7 @@ describe DiffHelper do
     end
 
     it 'should return js class when bottom lines should be unfolded' do
-      expect(unfold_bottom_class(true)).to eq('js-unfold-bottom')
+      expect(unfold_bottom_class(true)).to include('js-unfold-bottom')
     end
   end
 
diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb
index c0d2be98e85e6dad55069f3bc8c40c57fa13cee5..6b5e3d93d48da88f7c01d576bb4c07ff2126600c 100644
--- a/spec/helpers/events_helper_spec.rb
+++ b/spec/helpers/events_helper_spec.rb
@@ -57,7 +57,7 @@ describe EventsHelper do
       expected = '<pre class="code highlight js-syntax-highlight ruby">' \
         "<code><span class=\"k\">def</span> <span class=\"nf\">test</span>\n" \
         "  <span class=\"s1\">\'hello world\'</span>\n" \
-        "<span class=\"k\">end</span>" \
+        "<span class=\"k\">end</span>\n" \
         '</code></pre>'
       expect(helper.event_note(input)).to eq(expected)
     end
diff --git a/spec/javascripts/fixtures/issues_show.html.haml b/spec/javascripts/fixtures/issues_show.html.haml
index 470cabeafbb90718ad18d14423ef334ac3c61894..06c2ab1e823a87b97374d1a5d6a2e3a120303591 100644
--- a/spec/javascripts/fixtures/issues_show.html.haml
+++ b/spec/javascripts/fixtures/issues_show.html.haml
@@ -1,7 +1,7 @@
 :css
   .hidden { display: none !important; }
 
-.flash-container
+.flash-container.flash-container-page
   .flash-alert
   .flash-notice
 
diff --git a/spec/javascripts/project_title_spec.js.coffee b/spec/javascripts/project_title_spec.js.coffee
index f0d26fb54462e686b6fa143093265697d19d5fde..0244119fa0e794068055ca08804db9ff6e9cabb7 100644
--- a/spec/javascripts/project_title_spec.js.coffee
+++ b/spec/javascripts/project_title_spec.js.coffee
@@ -22,7 +22,7 @@ describe 'Project Title', ->
       @projects_data = fixture.load('projects.json')[0]
 
       spyOn(jQuery, 'ajax').and.callFake (req) =>
-        expect(req.url).toBe('/api/v3/projects.json')
+        expect(req.url).toBe('/api/v3/projects.json?simple=true')
         d = $.Deferred()
         d.resolve @projects_data
         d.promise()
diff --git a/spec/javascripts/u2f/authenticate_spec.coffee b/spec/javascripts/u2f/authenticate_spec.coffee
index e8a2892d67889003493d951ffb337f589d030b1e..8ffeda11704636d8869f0263e98640b77695feca 100644
--- a/spec/javascripts/u2f/authenticate_spec.coffee
+++ b/spec/javascripts/u2f/authenticate_spec.coffee
@@ -5,13 +5,12 @@
 #= 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 = new U2FAuthenticate(@container, {sign_requests: []}, "token")
     @component.start()
 
   it 'allows authenticating via a U2F device', ->
diff --git a/spec/javascripts/u2f/register_spec.js.coffee b/spec/javascripts/u2f/register_spec.js.coffee
index 0858abeca1aab86b940d15714be479dcf64c2459..87dc769792bd0461e4de084b4fecd9fcf1b447b6 100644
--- a/spec/javascripts/u2f/register_spec.js.coffee
+++ b/spec/javascripts/u2f/register_spec.js.coffee
@@ -5,7 +5,6 @@
 #= require ./mock_u2f_device
 
 describe 'U2FRegister', ->
-  U2FUtil.enableTestMode()
   fixture.load('u2f/register')
 
   beforeEach ->
diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index 9e3d2f5825d80b86d9e6e8f985bb242fd93992d5..9276a1540070e207feaecbe8b7126fa4d4fe70d5 100644
--- a/spec/lib/banzai/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -93,8 +93,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
     end
 
     it 'links with adjacent text' do
-      doc = reference_filter("Label (#{reference}.)")
-      expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\)))
+      doc = reference_filter("Label (#{reference}).")
+      expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\)\.))
     end
 
     it 'ignores invalid label names' do
@@ -104,8 +104,32 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
     end
   end
 
+  context 'String-based single-word references that begin with a digit' do
+    let(:label)     { create(:label, name: '2fa', project: project) }
+    let(:reference) { "#{Label.reference_prefix}#{label.name}" }
+
+    it 'links to a valid reference' do
+      doc = reference_filter("See #{reference}")
+
+      expect(doc.css('a').first.attr('href')).to eq urls.
+        namespace_project_issues_url(project.namespace, project, label_name: label.name)
+      expect(doc.text).to eq 'See 2fa'
+    end
+
+    it 'links with adjacent text' do
+      doc = reference_filter("Label (#{reference}).")
+      expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\)\.))
+    end
+
+    it 'ignores invalid label names' do
+      exp = act = "Label #{Label.reference_prefix}#{label.id}#{label.name.reverse}"
+
+      expect(reference_filter(act).to_html).to eq exp
+    end
+  end
+
   context 'String-based single-word references with special characters' do
-    let(:label)     { create(:label, name: '?gfm&', project: project) }
+    let(:label)     { create(:label, name: '?g.fm&', project: project) }
     let(:reference) { "#{Label.reference_prefix}#{label.name}" }
 
     it 'links to a valid reference' do
@@ -113,17 +137,17 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
 
       expect(doc.css('a').first.attr('href')).to eq urls.
         namespace_project_issues_url(project.namespace, project, label_name: label.name)
-      expect(doc.text).to eq 'See ?gfm&'
+      expect(doc.text).to eq 'See ?g.fm&'
     end
 
     it 'links with adjacent text' do
-      doc = reference_filter("Label (#{reference}.)")
-      expect(doc.to_html).to match(%r(\(<a.+><span.+>\?gfm&amp;</span></a>\.\)))
+      doc = reference_filter("Label (#{reference}).")
+      expect(doc.to_html).to match(%r(\(<a.+><span.+>\?g\.fm&amp;</span></a>\)\.))
     end
 
     it 'ignores invalid label names' do
       act = "Label #{Label.reference_prefix}#{label.name.reverse}"
-      exp = "Label #{Label.reference_prefix}&amp;mfg?"
+      exp = "Label #{Label.reference_prefix}&amp;mf.g?"
 
       expect(reference_filter(act).to_html).to eq exp
     end
@@ -153,8 +177,32 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
     end
   end
 
+  context 'String-based multi-word references that begin with a digit' do
+    let(:label)     { create(:label, name: '2 factor authentication', project: project) }
+    let(:reference) { label.to_reference(format: :name) }
+
+    it 'links to a valid reference' do
+      doc = reference_filter("See #{reference}")
+
+      expect(doc.css('a').first.attr('href')).to eq urls.
+        namespace_project_issues_url(project.namespace, project, label_name: label.name)
+      expect(doc.text).to eq 'See 2 factor authentication'
+    end
+
+    it 'links with adjacent text' do
+      doc = reference_filter("Label (#{reference}.)")
+      expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\)))
+    end
+
+    it 'ignores invalid label names' do
+      exp = act = "Label #{Label.reference_prefix}#{label.id}#{label.name.reverse}"
+
+      expect(reference_filter(act).to_html).to eq exp
+    end
+  end
+
   context 'String-based multi-word references with special characters in quotes' do
-    let(:label)     { create(:label, name: 'gfm & references?', project: project) }
+    let(:label)     { create(:label, name: 'g.fm & references?', project: project) }
     let(:reference) { label.to_reference(format: :name) }
 
     it 'links to a valid reference' do
@@ -162,22 +210,62 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
 
       expect(doc.css('a').first.attr('href')).to eq urls.
         namespace_project_issues_url(project.namespace, project, label_name: label.name)
-      expect(doc.text).to eq 'See gfm & references?'
+      expect(doc.text).to eq 'See g.fm & references?'
     end
 
     it 'links with adjacent text' do
       doc = reference_filter("Label (#{reference}.)")
-      expect(doc.to_html).to match(%r(\(<a.+><span.+>gfm &amp; references\?</span></a>\.\)))
+      expect(doc.to_html).to match(%r(\(<a.+><span.+>g\.fm &amp; references\?</span></a>\.\)))
     end
 
     it 'ignores invalid label names' do
       act = %(Label #{Label.reference_prefix}"#{label.name.reverse}")
-      exp = %(Label #{Label.reference_prefix}"?secnerefer &amp; mfg\")
+      exp = %(Label #{Label.reference_prefix}"?secnerefer &amp; mf.g\")
 
       expect(reference_filter(act).to_html).to eq exp
     end
   end
 
+  describe 'consecutive references' do
+    let(:bug) { create(:label, name: 'bug', project: project) }
+    let(:feature_proposal) { create(:label, name: 'feature proposal', project: project) }
+    let(:technical_debt) { create(:label, name: 'technical debt', project: project) }
+
+    let(:bug_reference) { "#{Label.reference_prefix}#{bug.name}" }
+    let(:feature_proposal_reference) { feature_proposal.to_reference(format: :name) }
+    let(:technical_debt_reference) { technical_debt.to_reference(format: :name) }
+
+    context 'separated with a comma' do
+      let(:references) { "#{bug_reference}, #{feature_proposal_reference}, #{technical_debt_reference}" }
+
+      it 'links to valid references' do
+        doc = reference_filter("See #{references}")
+
+        expect(doc.css('a').map { |a| a.attr('href') }).to match_array([
+          urls.namespace_project_issues_url(project.namespace, project, label_name: bug.name),
+          urls.namespace_project_issues_url(project.namespace, project, label_name: feature_proposal.name),
+          urls.namespace_project_issues_url(project.namespace, project, label_name: technical_debt.name)
+        ])
+        expect(doc.text).to eq 'See bug, feature proposal, technical debt'
+      end
+    end
+
+    context 'separated with a space' do
+      let(:references) { "#{bug_reference} #{feature_proposal_reference} #{technical_debt_reference}" }
+
+      it 'links to valid references' do
+        doc = reference_filter("See #{references}")
+
+        expect(doc.css('a').map { |a| a.attr('href') }).to match_array([
+          urls.namespace_project_issues_url(project.namespace, project, label_name: bug.name),
+          urls.namespace_project_issues_url(project.namespace, project, label_name: feature_proposal.name),
+          urls.namespace_project_issues_url(project.namespace, project, label_name: technical_debt.name)
+        ])
+        expect(doc.text).to eq 'See bug feature proposal technical debt'
+      end
+    end
+  end
+
   describe 'edge cases' do
     it 'gracefully handles non-references matching the pattern' do
       exp = act = '(format nil "~0f" 3.0) ; 3.0'
diff --git a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
index 407617f3307289a8c0de9c8caeeb1fcc669db0f4..b1370bca8332dfe2fbe95fddb2a89c7e7a8fad2b 100644
--- a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
+++ b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
@@ -3,15 +3,35 @@ require 'spec_helper'
 describe Banzai::Filter::SyntaxHighlightFilter, lib: true do
   include FilterSpecHelper
 
-  it 'highlights valid code blocks' do
-    result = filter('<pre><code>def fun end</code>')
-    expect(result.to_html).to eq("<pre class=\"code highlight js-syntax-highlight plaintext\"><code>def fun end</code></pre>\n")
+  context "when no language is specified" do
+    it "highlights as plaintext" do
+      result = filter('<pre><code>def fun end</code></pre>')
+      expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext"><code>def fun end</code></pre>')
+    end
   end
 
-  it 'passes through invalid code blocks' do
-    allow_any_instance_of(described_class).to receive(:block_code).and_raise(StandardError)
+  context "when a valid language is specified" do
+    it "highlights as that language" do
+      result = filter('<pre><code class="ruby">def fun end</code></pre>')
+      expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight ruby"><code><span class="k">def</span> <span class="nf">fun</span> <span class="k">end</span></code></pre>')
+    end
+  end
+
+  context "when an invalid language is specified" do
+    it "highlights as plaintext" do
+      result = filter('<pre><code class="gnuplot">This is a test</code></pre>')
+      expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext"><code>This is a test</code></pre>')
+    end
+  end
+
+  context "when Rouge formatting fails" do
+    before do
+      allow_any_instance_of(Rouge::Formatter).to receive(:format).and_raise(StandardError)
+    end
 
-    result = filter('<pre><code>This is a test</code></pre>')
-    expect(result.to_html).to eq('<pre>This is a test</pre>')
+    it "highlights as plaintext" do
+      result = filter('<pre><code class="ruby">This is a test</code></pre>')
+      expect(result.to_html).to eq('<pre class="code highlight"><code>This is a test</code></pre>')
+    end
   end
 end
diff --git a/spec/lib/banzai/object_renderer_spec.rb b/spec/lib/banzai/object_renderer_spec.rb
index 44256b32bdc7768b58a475fb227f8821314f4c1d..bcdb95250caee162af0ffc151c907ffa832b6add 100644
--- a/spec/lib/banzai/object_renderer_spec.rb
+++ b/spec/lib/banzai/object_renderer_spec.rb
@@ -17,6 +17,7 @@ describe Banzai::ObjectRenderer do
         and_call_original
 
       expect(object).to receive(:note_html=).with('<p>hello</p>')
+      expect(object).to receive(:user_visible_reference_count=).with(0)
 
       renderer.render([object], :note)
     end
@@ -25,9 +26,10 @@ describe Banzai::ObjectRenderer do
   describe '#render_objects' do
     it 'renders an Array of objects' do
       object = double(:object, note: 'hello')
+
       renderer = described_class.new(project, user)
 
-      expect(renderer).to receive(:render_attribute).with(object, :note).
+      expect(renderer).to receive(:render_attributes).with([object], :note).
         and_call_original
 
       rendered = renderer.render_objects([object], :note)
@@ -38,7 +40,7 @@ describe Banzai::ObjectRenderer do
   end
 
   describe '#redact_documents' do
-    it 'redacts a set of documents and returns them as an Array of Strings' do
+    it 'redacts a set of documents and returns them as an Array of Hashes' do
       doc = Nokogiri::HTML.fragment('<p>hello</p>')
       renderer = described_class.new(project, user)
 
@@ -48,7 +50,9 @@ describe Banzai::ObjectRenderer do
 
       redacted = renderer.redact_documents([doc])
 
-      expect(redacted).to eq(['<p>hello</p>'])
+      expect(redacted.count).to eq(1)
+      expect(redacted.first[:visible_reference_count]).to eq(0)
+      expect(redacted.first[:document].to_html).to eq('<p>hello</p>')
     end
   end
 
@@ -85,14 +89,36 @@ describe Banzai::ObjectRenderer do
     end
   end
 
-  describe '#render_attribute' do
-    it 'renders the attribute of an object' do
-      object = double(:doc, note: 'hello')
+  describe '#render_attributes' do
+    it 'renders the attribute of a list of objects' do
+      objects = [double(:doc, note: 'hello'), double(:doc, note: 'bye')]
       renderer = described_class.new(project, user, pipeline: :note)
-      doc = renderer.render_attribute(object, :note)
 
-      expect(doc).to be_an_instance_of(Nokogiri::HTML::DocumentFragment)
-      expect(doc.to_html).to eq('<p>hello</p>')
+      expect(Banzai).to receive(:cache_collection_render).
+        with([
+          { text: 'hello', context: renderer.context_for(objects[0], :note) },
+          { text: 'bye', context: renderer.context_for(objects[1], :note) }
+        ]).
+        and_call_original
+
+      docs = renderer.render_attributes(objects, :note)
+
+      expect(docs[0]).to be_an_instance_of(Nokogiri::HTML::DocumentFragment)
+      expect(docs[0].to_html).to eq('<p>hello</p>')
+
+      expect(docs[1]).to be_an_instance_of(Nokogiri::HTML::DocumentFragment)
+      expect(docs[1].to_html).to eq('<p>bye</p>')
+    end
+
+    it 'returns when no objects to render' do
+      objects = []
+      renderer = described_class.new(project, user, pipeline: :note)
+
+      expect(Banzai).to receive(:cache_collection_render).
+        with([]).
+        and_call_original
+
+      expect(renderer.render_attributes(objects, :note)).to eq([])
     end
   end
 
diff --git a/spec/lib/banzai/redactor_spec.rb b/spec/lib/banzai/redactor_spec.rb
index 488f465bcdaf181f3257bcafdbfd4a2a9ad3416f..254657a881da3a40a038dbca0adfe06e85e44c9b 100644
--- a/spec/lib/banzai/redactor_spec.rb
+++ b/spec/lib/banzai/redactor_spec.rb
@@ -15,11 +15,31 @@ describe Banzai::Redactor do
 
       expect(redactor).to receive(:nodes_visible_to_user).and_return([])
 
-      expect(redactor.redact([doc1, doc2])).to eq([doc1, doc2])
+      redacted_data = redactor.redact([doc1, doc2])
 
+      expect(redacted_data.map { |data| data[:document] }).to eq([doc1, doc2])
+      expect(redacted_data.map { |data| data[:visible_reference_count] }).to eq([0, 0])
       expect(doc1.to_html).to eq('foo')
       expect(doc2.to_html).to eq('bar')
     end
+
+    it 'does not redact an Array of documents' do
+      doc1_html = '<a class="gfm" data-reference-type="issue">foo</a>'
+      doc1 = Nokogiri::HTML.fragment(doc1_html)
+
+      doc2_html = '<a class="gfm" data-reference-type="issue">bar</a>'
+      doc2 = Nokogiri::HTML.fragment(doc2_html)
+
+      nodes = redactor.document_nodes([doc1, doc2]).map { |x| x[:nodes] }
+      expect(redactor).to receive(:nodes_visible_to_user).and_return(nodes.flatten)
+
+      redacted_data = redactor.redact([doc1, doc2])
+
+      expect(redacted_data.map { |data| data[:document] }).to eq([doc1, doc2])
+      expect(redacted_data.map { |data| data[:visible_reference_count] }).to eq([1, 1])
+      expect(doc1.to_html).to eq(doc1_html)
+      expect(doc2.to_html).to eq(doc2_html)
+    end
   end
 
   describe '#redact_nodes' do
@@ -31,7 +51,7 @@ describe Banzai::Redactor do
         with([node]).
         and_return(Set.new)
 
-      redactor.redact_nodes([node])
+      redactor.redact_document_nodes([{ document: doc, nodes: [node] }])
 
       expect(doc.to_html).to eq('foo')
     end
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index bad439bc48937e990fa883eef7717647da5133de..ad6587b4c25e28ba387d496b1940120655b605ae 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -19,19 +19,18 @@ module Ci
         expect(config_processor.builds_for_stage_and_ref(type, "master").first).to eq({
           stage: "test",
           stage_idx: 1,
-          except: nil,
           name: :rspec,
-          only: nil,
           commands: "pwd\nrspec",
           tag_list: [],
           options: {},
           allow_failure: false,
           when: "on_success",
           environment: nil,
+          yaml_variables: []
         })
       end
 
-      describe :only do
+      describe 'only' do
         it "does not return builds if only has another branch" do
           config = YAML.dump({
                                before_script: ["pwd"],
@@ -187,7 +186,7 @@ module Ci
         end
       end
 
-      describe :except do
+      describe 'except' do
         it "returns builds if except has another branch" do
           config = YAML.dump({
                                before_script: ["pwd"],
@@ -432,11 +431,9 @@ module Ci
 
         expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
         expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
-          except: nil,
           stage: "test",
           stage_idx: 1,
           name: :rspec,
-          only: nil,
           commands: "pwd\nrspec",
           tag_list: [],
           options: {
@@ -446,6 +443,7 @@ module Ci
           allow_failure: false,
           when: "on_success",
           environment: nil,
+          yaml_variables: []
         })
       end
 
@@ -461,11 +459,9 @@ module Ci
 
         expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
         expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
-          except: nil,
           stage: "test",
           stage_idx: 1,
           name: :rspec,
-          only: nil,
           commands: "pwd\nrspec",
           tag_list: [],
           options: {
@@ -475,101 +471,126 @@ module Ci
           allow_failure: false,
           when: "on_success",
           environment: nil,
+          yaml_variables: []
         })
       end
     end
 
     describe 'Variables' do
-      context 'when global variables are defined' do
-        it 'returns global variables' do
-          variables = {
-            VAR1: 'value1',
-            VAR2: 'value2',
-          }
+      let(:config_processor) { GitlabCiYamlProcessor.new(YAML.dump(config), path) }
 
-          config = YAML.dump({
+      subject { config_processor.builds.first[:yaml_variables] }
+
+      context 'when global variables are defined' do
+        let(:variables) do
+          { VAR1: 'value1', VAR2: 'value2' }
+        end
+        let(:config) do
+          {
             variables: variables,
             before_script: ['pwd'],
             rspec: { script: 'rspec' }
-          })
+          }
+        end
 
-          config_processor = GitlabCiYamlProcessor.new(config, path)
+        it 'returns global variables' do
+          expect(subject).to contain_exactly(
+            { key: :VAR1, value: 'value1', public: true },
+            { key: :VAR2, value: 'value2', public: true }
+          )
+        end
+      end
+
+      context 'when job and global variables are defined' do
+        let(:global_variables) do
+          { VAR1: 'global1', VAR3: 'global3' }
+        end
+        let(:job_variables) do
+          { VAR1: 'value1', VAR2: 'value2' }
+        end
+        let(:config) do
+          {
+            before_script: ['pwd'],
+            variables: global_variables,
+            rspec: { script: 'rspec', variables: job_variables }
+          }
+        end
 
-          expect(config_processor.global_variables).to eq(variables)
+        it 'returns all unique variables' do
+          expect(subject).to contain_exactly(
+            { key: :VAR3, value: 'global3', public: true },
+            { key: :VAR1, value: 'value1', public: true },
+            { key: :VAR2, value: 'value2', public: true }
+          )
         end
       end
 
       context 'when job variables are defined' do
-        context 'when syntax is correct' do
-          it 'returns job variables' do
-            variables = {
-              KEY1: 'value1',
-              SOME_KEY_2: 'value2'
-            }
+        let(:config) do
+          {
+            before_script: ['pwd'],
+            rspec: { script: 'rspec', variables: variables }
+          }
+        end
+
+        context 'when also global variables are defined' do
 
-            config = YAML.dump(
-              { before_script: ['pwd'],
-                rspec: {
-                  variables: variables,
-                  script: 'rspec' }
-              })
+        end
 
-            config_processor = GitlabCiYamlProcessor.new(config, path)
+        context 'when syntax is correct' do
+          let(:variables) do
+            { VAR1: 'value1', VAR2: 'value2' }
+          end
 
-            expect(config_processor.job_variables(:rspec)).to eq variables
+          it 'returns job variables' do
+            expect(subject).to contain_exactly(
+              { key: :VAR1, value: 'value1', public: true },
+              { key: :VAR2, value: 'value2', public: true }
+            )
           end
         end
 
         context 'when syntax is incorrect' do
           context 'when variables defined but invalid' do
-            it 'raises error' do
-              variables = [:KEY1, 'value1', :KEY2, 'value2']
-
-              config =  YAML.dump(
-                { before_script: ['pwd'],
-                  rspec: {
-                    variables: variables,
-                    script: 'rspec' }
-                })
+            let(:variables) do
+              [ :VAR1, 'value1', :VAR2, 'value2' ]
+            end
 
-              expect { GitlabCiYamlProcessor.new(config, path) }
+            it 'raises error' do
+              expect { subject }
                 .to raise_error(GitlabCiYamlProcessor::ValidationError,
-                                 /job: variables should be a map/)
+                                /job: variables should be a map/)
             end
           end
 
           context 'when variables key defined but value not specified' do
-            it 'returns empty array' do
-              config =  YAML.dump(
-                { before_script: ['pwd'],
-                  rspec: {
-                    variables: nil,
-                    script: 'rspec' }
-                })
-
-              config_processor = GitlabCiYamlProcessor.new(config, path)
+            let(:variables) do
+              nil
+            end
 
+            it 'returns empty array' do
               ##
               # When variables config is empty, we assume this is a valid
               # configuration, see issue #18775
               #
-              expect(config_processor.job_variables(:rspec))
-                .to be_an_instance_of(Array).and be_empty
+              expect(subject).to be_an_instance_of(Array)
+              expect(subject).to be_empty
             end
           end
         end
       end
 
       context 'when job variables are not defined' do
-        it 'returns empty array' do
-          config = YAML.dump({
+        let(:config) do
+          {
             before_script: ['pwd'],
             rspec: { script: 'rspec' }
-          })
-
-          config_processor = GitlabCiYamlProcessor.new(config, path)
+          }
+        end
 
-          expect(config_processor.job_variables(:rspec)).to eq []
+        it 'returns empty array' do
+          expect(subject).to be_an_instance_of(Array)
+          expect(subject).to be_empty
         end
       end
     end
@@ -681,11 +702,9 @@ module Ci
 
         expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
         expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
-          except: nil,
           stage: "test",
           stage_idx: 1,
           name: :rspec,
-          only: nil,
           commands: "pwd\nrspec",
           tag_list: [],
           options: {
@@ -701,6 +720,7 @@ module Ci
           when: "on_success",
           allow_failure: false,
           environment: nil,
+          yaml_variables: []
         })
       end
 
@@ -819,17 +839,16 @@ module Ci
         it "doesn't create jobs that start with dot" do
           expect(subject.size).to eq(1)
           expect(subject.first).to eq({
-            except: nil,
             stage: "test",
             stage_idx: 1,
             name: :normal_job,
-            only: nil,
             commands: "test",
             tag_list: [],
             options: {},
             when: "on_success",
             allow_failure: false,
             environment: nil,
+            yaml_variables: []
           })
         end
       end
@@ -865,30 +884,28 @@ module Ci
         it "is correctly supported for jobs" do
           expect(subject.size).to eq(2)
           expect(subject.first).to eq({
-            except: nil,
             stage: "build",
             stage_idx: 0,
             name: :job1,
-            only: nil,
             commands: "execute-script-for-job",
             tag_list: [],
             options: {},
             when: "on_success",
             allow_failure: false,
             environment: nil,
+            yaml_variables: []
           })
           expect(subject.second).to eq({
-            except: nil,
             stage: "build",
             stage_idx: 0,
             name: :job2,
-            only: nil,
             commands: "execute-script-for-job",
             tag_list: [],
             options: {},
             when: "on_success",
             allow_failure: false,
             environment: nil,
+            yaml_variables: []
           })
         end
       end
diff --git a/spec/lib/container_registry/blob_spec.rb b/spec/lib/container_registry/blob_spec.rb
index 4d8cb787ddeef5924d15064bfbe89933f0440793..bbacdc67ebd30d8dd7eb2c39da9cebf4762147b0 100644
--- a/spec/lib/container_registry/blob_spec.rb
+++ b/spec/lib/container_registry/blob_spec.rb
@@ -9,8 +9,9 @@ describe ContainerRegistry::Blob do
       'size' => 1000
     }
   end
+  let(:token) { 'authorization-token' }
   
-  let(:registry) { ContainerRegistry::Registry.new('http://example.com') }
+  let(:registry) { ContainerRegistry::Registry.new('http://example.com', token: token) }
   let(:repository) { registry.repository('group/test') }
   let(:blob) { repository.blob(config) }
 
@@ -58,4 +59,53 @@ describe ContainerRegistry::Blob do
 
     it { is_expected.to be_truthy }
   end
+
+  context '#data' do
+    let(:data) { '{"key":"value"}' }
+
+    subject { blob.data }
+
+    context 'when locally stored' do
+      before do
+        stub_request(:get, 'http://example.com/v2/group/test/blobs/sha256:0123456789012345').
+          to_return(
+            status: 200,
+            headers: { 'Content-Type' => 'application/json' },
+            body: data)
+      end
+
+      it { is_expected.to eq(data) }
+    end
+
+    context 'when externally stored' do
+      before do
+        stub_request(:get, 'http://example.com/v2/group/test/blobs/sha256:0123456789012345').
+          with(headers: { 'Authorization' => "bearer #{token}" }).
+          to_return(
+            status: 307,
+            headers: { 'Location' => location })
+      end
+
+      context 'for a valid address' do
+        let(:location) { 'http://external.com/blob/file' }
+
+        before do
+          stub_request(:get, location).
+            with(headers: { 'Authorization' => nil }).
+            to_return(
+              status: 200,
+              headers: { 'Content-Type' => 'application/json' },
+              body: data)
+        end
+
+        it { is_expected.to eq(data) }
+      end
+
+      context 'for invalid file' do
+        let(:location) { 'file:///etc/passwd' }
+
+        it { expect{ subject }.to raise_error(ArgumentError, 'invalid address') }
+      end
+    end
+  end
 end
diff --git a/spec/lib/container_registry/tag_spec.rb b/spec/lib/container_registry/tag_spec.rb
index c7324c2bf7726554615a5f800880c966192479c1..c5e31ae82b6a2cf1d709bafef3bc39bb8385b6b0 100644
--- a/spec/lib/container_registry/tag_spec.rb
+++ b/spec/lib/container_registry/tag_spec.rb
@@ -77,24 +77,47 @@ describe ContainerRegistry::Tag do
       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
+        shared_examples 'a processable' do
+          context '#config' do
+            subject { tag.config }
 
-        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.not_to be_nil }
+            it { is_expected.not_to be_nil }
+          end
         end
 
-        context '#created_at' do
-          subject { tag.created_at }
+        context 'when locally stored' 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
+
+          it_behaves_like 'a processable'
+        end
 
-          it { is_expected.not_to be_nil }
+        context 'when externally stored' do
+          before do
+            stub_request(:get, 'http://example.com/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac').
+              with(headers: { 'Accept' => 'application/octet-stream' }).
+              to_return(
+                status: 307,
+                headers: { 'Location' => 'http://external.com/blob/file' })
+
+            stub_request(:get, 'http://external.com/blob/file').
+              to_return(
+                status: 200,
+                body: File.read(Rails.root + 'spec/fixtures/container_registry/config_blob.json'))
+          end
+
+          it_behaves_like 'a processable'
         end
       end
     end
diff --git a/spec/lib/gitlab/bitbucket_import/client_spec.rb b/spec/lib/gitlab/bitbucket_import/client_spec.rb
index 760d66a148883cfe786dc3b8732078da9ee6d410..7543c29bcc449f5f94551b4cfc90f497415cfa5d 100644
--- a/spec/lib/gitlab/bitbucket_import/client_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/client_spec.rb
@@ -54,12 +54,12 @@ describe Gitlab::BitbucketImport::Client, lib: true do
   context 'project import' do
     it 'calls .from_project with no errors' do
       project = create(:empty_project)
+      project.import_url = "ssh://git@bitbucket.org/test/test.git"
       project.create_or_update_import_data(credentials:
                                              { user: "git",
                                                password: nil,
                                                bb_session: { bitbucket_access_token: "test",
                                                              bitbucket_access_token_secret: "test" } })
-      project.import_url = "ssh://git@bitbucket.org/test/test.git"
 
       expect { described_class.from_project(project) }.not_to raise_error
     end
diff --git a/spec/lib/gitlab/build_data_builder_spec.rb b/spec/lib/gitlab/build_data_builder_spec.rb
index 38be9448794ee07aee275fdfee80f2b7a6d1a268..23ae5cfacc4c0e256cb8d449b01679ff06a1513c 100644
--- a/spec/lib/gitlab/build_data_builder_spec.rb
+++ b/spec/lib/gitlab/build_data_builder_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
 describe 'Gitlab::BuildDataBuilder' do
   let(:build) { create(:ci_build) }
 
-  describe :build do
+  describe '.build' do
     let(:data) do
       Gitlab::BuildDataBuilder.build(build)
     end
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 9096ad101b0ba677a28282b85a8544c52e1553eb..4ec3f19e03fb55bb2c3714718d8df5e4976e0c35 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -13,6 +13,10 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
     context 'outside a transaction' do
       before do
         expect(model).to receive(:transaction_open?).and_return(false)
+
+        unless Gitlab::Database.postgresql?
+          allow_any_instance_of(Gitlab::Database::MigrationHelpers).to receive(:disable_statement_timeout)
+        end
       end
 
       context 'using PostgreSQL' do
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index 1cb513d5229fec358db273ea4320807b39805c47..0460dcf4658afd8fa0be42def6d40591de575eaa 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -8,14 +8,14 @@ describe Gitlab::Diff::File, lib: true do
   let(:diff) { commit.diffs.first }
   let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs: commit.diff_refs, repository: project.repository) }
 
-  describe :diff_lines do
+  describe '#diff_lines' do
     let(:diff_lines) { diff_file.diff_lines }
 
     it { expect(diff_lines.size).to eq(30) }
     it { expect(diff_lines.first).to be_kind_of(Gitlab::Diff::Line) }
   end
 
-  describe :mode_changed? do
+  describe '#mode_changed?' do
     it { expect(diff_file.mode_changed?).to be_falsey }
   end
 
diff --git a/spec/lib/gitlab/diff/highlight_spec.rb b/spec/lib/gitlab/diff/highlight_spec.rb
index fb5d50a5c6823b8eb5e5177edb5b240de3ace056..88e4115c4532fb04c0cf3447a81ffb3ceae83011 100644
--- a/spec/lib/gitlab/diff/highlight_spec.rb
+++ b/spec/lib/gitlab/diff/highlight_spec.rb
@@ -28,13 +28,13 @@ describe Gitlab::Diff::Highlight, lib: true do
       end
 
       it 'highlights and marks removed lines' do
-        code = %Q{-<span id="LC9" class="line">      <span class="k">raise</span> <span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span>\n}
+        code = %Q{-<span id="LC9" class="line">      <span class="k">raise</span> <span class="s2">"System commands must be given as an array of strings"</span></span>\n}
 
         expect(subject[4].text).to eq(code)
       end
 
       it 'highlights and marks added lines' do
-        code = %Q{+<span id="LC9" class="line">      <span class="k">raise</span> <span class="no"><span class='idiff left'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff right'> </span><span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span>\n}
+        code = %Q{+<span id="LC9" class="line">      <span class="k">raise</span> <span class="no"><span class='idiff left'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff right'> </span><span class="s2">"System commands must be given as an array of strings"</span></span>\n}
 
         expect(subject[5].text).to eq(code)
       end
diff --git a/spec/lib/gitlab/diff/inline_diff_spec.rb b/spec/lib/gitlab/diff/inline_diff_spec.rb
index 95a993d26cf70be4c9be3f01a688cb7278b75e5a..8ca3f73509e471c8be4402df601cd6f9c7f61f3f 100644
--- a/spec/lib/gitlab/diff/inline_diff_spec.rb
+++ b/spec/lib/gitlab/diff/inline_diff_spec.rb
@@ -3,14 +3,19 @@ require 'spec_helper'
 describe Gitlab::Diff::InlineDiff, lib: true do
   describe '.for_lines' do
     let(:diff) do
-      <<eos
- class Test
--  def initialize(test = true)
-+  def initialize(test = false)
-     @test = test
-   end
- end
-eos
+      <<-EOF.strip_heredoc
+         class Test
+        -  def initialize(test = true)
+        +  def initialize(test = false)
+             @test = test
+        -    if true
+        -      @foo = "bar"
+        +    unless false
+        +      @foo = "baz"
+             end
+           end
+         end
+      EOF
     end
 
     let(:subject) { described_class.for_lines(diff.lines) }
@@ -20,8 +25,11 @@ eos
       expect(subject[1]).to eq([25..27])
       expect(subject[2]).to eq([25..28])
       expect(subject[3]).to be_nil
-      expect(subject[4]).to be_nil
-      expect(subject[5]).to be_nil
+      expect(subject[4]).to eq([5..10])
+      expect(subject[5]).to eq([17..17])
+      expect(subject[6]).to eq([5..15])
+      expect(subject[7]).to eq([17..17])
+      expect(subject[8]).to be_nil
     end
   end
 
diff --git a/spec/lib/gitlab/diff/parser_spec.rb b/spec/lib/gitlab/diff/parser_spec.rb
index cdff063a9ed548d5dfa75fa9124a27b3eaeaebd1..c33596276522c9532f26a1f9c8f4fdb0bc02183f 100644
--- a/spec/lib/gitlab/diff/parser_spec.rb
+++ b/spec/lib/gitlab/diff/parser_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::Diff::Parser, lib: true do
   let(:diff) { commit.diffs.first }
   let(:parser) { Gitlab::Diff::Parser.new }
 
-  describe :parse do
+  describe '#parse' do
     let(:diff) do
       <<eos
 --- a/files/ruby/popen.rb
diff --git a/spec/lib/gitlab/github_import/client_spec.rb b/spec/lib/gitlab/github_import/client_spec.rb
index 3b023a3544681b0d9386065f49f8f376a99778d6..613c47d55f17ee0f23f667d7a01f0e8cdf708d80 100644
--- a/spec/lib/gitlab/github_import/client_spec.rb
+++ b/spec/lib/gitlab/github_import/client_spec.rb
@@ -61,4 +61,11 @@ describe Gitlab::GithubImport::Client, lib: true do
       expect(client.api.api_endpoint).to eq 'https://github.company.com/'
     end
   end
+
+  it 'does not raise error when rate limit is disabled' do
+    stub_request(:get, /api.github.com/)
+    allow(client.api).to receive(:rate_limit!).and_raise(Octokit::NotFound)
+
+    expect { client.issues }.not_to raise_error
+  end
 end
diff --git a/spec/lib/gitlab/gitlab_import/importer_spec.rb b/spec/lib/gitlab/gitlab_import/importer_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d3f1deb383765b221f5f5faaeb60a23d1d5d052e
--- /dev/null
+++ b/spec/lib/gitlab/gitlab_import/importer_spec.rb
@@ -0,0 +1,53 @@
+require 'spec_helper'
+
+describe Gitlab::GitlabImport::Importer, lib: true do
+  include ImportSpecHelper
+
+  describe '#execute' do
+    before do
+      stub_omniauth_provider('gitlab')
+      stub_request('issues', [
+        {
+          'id' => 2579857,
+          'iid' => 3,
+          'title' => 'Issue',
+          'description' => 'Lorem ipsum',
+          'state' => 'opened',
+          'author' => {
+            'id' => 283999,
+            'name' => 'John Doe'
+          }
+        }
+      ])
+      stub_request('issues/2579857/notes', [])
+    end
+
+    it 'persists issues' do
+      project = create(:empty_project, import_source: 'asd/vim')
+      project.build_import_data(credentials: { password: 'password' })
+
+      subject = described_class.new(project)
+      subject.execute
+
+      expected_attributes = {
+        iid: 3,
+        title: 'Issue',
+        description: "*Created by: John Doe*\n\nLorem ipsum",
+        state: 'opened',
+        author_id: project.creator_id
+      }
+
+      expect(project.issues.first).to have_attributes(expected_attributes)
+    end
+
+    def stub_request(path, body)
+      url = "https://gitlab.com/api/v3/projects/asd%2Fvim/#{path}?page=1&per_page=100"
+
+      WebMock.stub_request(:get, url).
+        to_return(
+          headers: { 'Content-Type' => 'application/json' },
+          body: body
+        )
+    end
+  end
+end
diff --git a/spec/lib/gitlab/import_export/import_export_spec.rb b/spec/lib/gitlab/import_export/import_export_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d6409a2955086b6ac6e78f9d1ececdb0223a6fe8
--- /dev/null
+++ b/spec/lib/gitlab/import_export/import_export_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport, services: true do
+  describe 'export filename' do
+    let(:project) { create(:project, :public, path: 'project-path') }
+
+    it 'contains the project path' do
+      expect(described_class.export_filename(project: project)).to include(project.path)
+    end
+
+    it 'contains the namespace path' do
+      expect(described_class.export_filename(project: project)).to include(project.namespace.path)
+    end
+
+    it 'does not go over a certain length' do
+      project.path = 'a' * 100
+
+      expect(described_class.export_filename(project: project).length).to be < 70
+    end
+  end
+end
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index 0b30e8c9b0499aff119947332742056f9c6513aa..4113d829c3c60f04a244cb47f29b1439a14585dd 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -26,6 +26,7 @@
       "deleted_at": null,
       "due_date": null,
       "moved_to_id": null,
+      "test_ee_field": "test",
       "notes": [
         {
           "id": 351,
@@ -4208,7 +4209,18 @@
             "name": "User 4"
           },
           "events": [
-
+            {
+              "id": 529,
+              "target_type": "Note",
+              "target_id": 2521,
+              "title": "test levels",
+              "data": null,
+              "project_id": 4,
+              "created_at": "2016-07-07T14:35:12.128Z",
+              "updated_at": "2016-07-07T14:35:12.128Z",
+              "action": 6,
+              "author_id": 1
+            }
           ]
         },
         {
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index a72aaa44e823efd418d18566e9e3a8b5e562a53b..877be300262ae4289e1fbc6969ab2eb3d0a81e4e 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -24,11 +24,35 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
         expect(Ci::Pipeline.first.notes).not_to be_empty
       end
 
-      it 'restores the correct event' do
+      it 'restores the correct event with symbolised data' do
         restored_project_json
 
         expect(Event.where.not(data: nil).first.data[:ref]).not_to be_empty
       end
+
+      it 'preserves updated_at on issues' do
+        restored_project_json
+
+        issue = Issue.where(description: 'Aliquam enim illo et possimus.').first
+
+        expect(issue.reload.updated_at.to_s).to eq('2016-06-14 15:02:47 UTC')
+      end
+
+      context 'event at forth level of the tree' do
+        let(:event) { Event.where(title: 'test levels').first }
+
+        before do
+          restored_project_json
+        end
+
+        it 'restores the event' do
+          expect(event).not_to be_nil
+        end
+
+        it 'event belongs to note, belongs to merge request, belongs to a project' do
+          expect(event.note.noteable.project).not_to be_nil
+        end
+      end
     end
   end
 end
diff --git a/spec/lib/gitlab/ldap/access_spec.rb b/spec/lib/gitlab/ldap/access_spec.rb
index f5b66b8156f0739ab87e5be0d9cee4089fc951ba..acd5394382c72a77e5488498a2f5b9dd685a2952 100644
--- a/spec/lib/gitlab/ldap/access_spec.rb
+++ b/spec/lib/gitlab/ldap/access_spec.rb
@@ -4,7 +4,7 @@ describe Gitlab::LDAP::Access, lib: true do
   let(:access) { Gitlab::LDAP::Access.new user }
   let(:user) { create(:omniauth_user) }
 
-  describe :allowed? do
+  describe '#allowed?' do
     subject { access.allowed? }
 
     context 'when the user cannot be found' do
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index 03199a2523e288d77e39e24780c4c1a4fabc284a..949f6e2b19a3ee76af0ef57b29455a5f2353f2c2 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -25,7 +25,7 @@ describe Gitlab::LDAP::User, lib: true do
     OmniAuth::AuthHash.new(uid: 'my-uid', provider: 'ldapmain', info: info_upper_case)
   end
 
-  describe :changed? do
+  describe '#changed?' do
     it "marks existing ldap user as changed" do
       create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain')
       expect(ldap_user.changed?).to be_truthy
diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb
deleted file mode 100644
index 659facd6c19e663dfa06f4b061c5c2b80cd227e2..0000000000000000000000000000000000000000
--- a/spec/lib/gitlab/lfs/lfs_router_spec.rb
+++ /dev/null
@@ -1,730 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Lfs::Router, lib: true do
-  let(:project) { create(:project) }
-  let(:public_project) { create(:project, :public) }
-  let(:forked_project) { fork_project(public_project, user) }
-
-  let(:user) { create(:user) }
-  let(:user_two) { create(:user) }
-  let!(:lfs_object) { create(:lfs_object, :with_file) }
-
-  let(:request) { Rack::Request.new(env) }
-  let(:env) do
-    {
-      'rack.input'     => '',
-      'REQUEST_METHOD' => 'GET',
-    }
-  end
-
-  let(:lfs_router_auth) { new_lfs_router(project, user: user) }
-  let(:lfs_router_ci_auth) { new_lfs_router(project, ci: true) }
-  let(:lfs_router_noauth) { new_lfs_router(project) }
-  let(:lfs_router_public_auth) { new_lfs_router(public_project, user: user) }
-  let(:lfs_router_public_ci_auth) { new_lfs_router(public_project, ci: true) }
-  let(:lfs_router_public_noauth) { new_lfs_router(public_project) }
-  let(:lfs_router_forked_noauth) { new_lfs_router(forked_project) }
-  let(:lfs_router_forked_auth) { new_lfs_router(forked_project, user: user_two) }
-  let(:lfs_router_forked_ci_auth) { new_lfs_router(forked_project, ci: true) }
-
-  let(:sample_oid) { "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80" }
-  let(:sample_size) { 499013 }
-  let(:respond_with_deprecated) {[ 501, { "Content-Type" => "application/json; charset=utf-8" }, ["{\"message\":\"Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]}
-  let(:respond_with_disabled) {[ 501, { "Content-Type" => "application/json; charset=utf-8" }, ["{\"message\":\"Git LFS is not enabled on this GitLab server, contact your admin.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]}
-
-  describe 'when lfs is disabled' do
-    before do
-      allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
-      env['REQUEST_METHOD'] = 'POST'
-      body = {
-                'objects' => [
-                  { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
-                    'size' => 1575078
-                  },
-                  { 'oid' => sample_oid,
-                    'size' => sample_size
-                  }
-                ],
-                'operation' => 'upload'
-              }.to_json
-      env['rack.input'] = StringIO.new(body)
-      env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch"
-    end
-
-    it 'responds with 501' do
-      expect(lfs_router_auth.try_call).to match_array(respond_with_disabled)
-    end
-  end
-
-  describe 'when fetching lfs object using deprecated API' do
-    before do
-      enable_lfs
-      env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}"
-    end
-
-    it 'responds with 501' do
-      expect(lfs_router_auth.try_call).to match_array(respond_with_deprecated)
-    end
-  end
-
-  describe 'when fetching lfs object' do
-    before do
-      enable_lfs
-      env['HTTP_ACCEPT'] = "application/vnd.git-lfs+json; charset=utf-8"
-      env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}"
-    end
-
-    describe 'and request comes from gitlab-workhorse' do
-      context 'without user being authorized' do
-        it "responds with status 401" do
-          expect(lfs_router_noauth.try_call.first).to eq(401)
-        end
-      end
-
-      context 'with required headers' do
-        before do
-          project.lfs_objects << lfs_object
-          env['HTTP_X_SENDFILE_TYPE'] = "X-Sendfile"
-        end
-
-        context 'when user does not have project access' do
-          it "responds with status 403" do
-            expect(lfs_router_auth.try_call.first).to eq(403)
-          end
-        end
-
-        context 'when user has project access' do
-          before do
-            project.team << [user, :master]
-          end
-
-          it "responds with status 200" do
-            expect(lfs_router_auth.try_call.first).to eq(200)
-          end
-
-          it "responds with the file location" do
-            expect(lfs_router_auth.try_call[1]['Content-Type']).to eq("application/octet-stream")
-            expect(lfs_router_auth.try_call[1]['X-Sendfile']).to eq(lfs_object.file.path)
-          end
-        end
-
-        context 'when CI is authorized' do
-          it "responds with status 200" do
-            expect(lfs_router_ci_auth.try_call.first).to eq(200)
-          end
-
-          it "responds with the file location" do
-            expect(lfs_router_ci_auth.try_call[1]['Content-Type']).to eq("application/octet-stream")
-            expect(lfs_router_ci_auth.try_call[1]['X-Sendfile']).to eq(lfs_object.file.path)
-          end
-        end
-      end
-
-      context 'without required headers' do
-        it "responds with status 403" do
-          expect(lfs_router_auth.try_call.first).to eq(403)
-        end
-      end
-    end
-  end
-
-  describe 'when handling lfs request using deprecated API' do
-    before do
-      enable_lfs
-      env['REQUEST_METHOD'] = 'POST'
-      env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects"
-    end
-
-    it 'responds with 501' do
-      expect(lfs_router_auth.try_call).to match_array(respond_with_deprecated)
-    end
-  end
-
-  describe 'when handling lfs batch request' do
-    before do
-      enable_lfs
-      env['REQUEST_METHOD'] = 'POST'
-      env['PATH_INFO'] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch"
-    end
-
-    describe 'download' do
-      before do
-        body = { 'operation' => 'download',
-                 'objects' => [
-                   { 'oid' => sample_oid,
-                     'size' => sample_size
-                   }]
-        }.to_json
-        env['rack.input'] = StringIO.new(body)
-      end
-
-      shared_examples 'an authorized requests' do
-        context 'when downloading an lfs object that is assigned to our project' do
-          before do
-            project.lfs_objects << lfs_object
-          end
-
-          it 'responds with status 200 and href to download' do
-            response = router.try_call
-            expect(response.first).to eq(200)
-            response_body = ActiveSupport::JSON.decode(response.last.first)
-
-            expect(response_body).to eq('objects' => [
-              { 'oid' => sample_oid,
-                'size' => sample_size,
-                'actions' => {
-                  'download' => {
-                    'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
-                    'header' => { 'Authorization' => auth }
-                  }
-                }
-              }])
-          end
-        end
-
-        context 'when downloading an lfs object that is assigned to other project' do
-          before do
-            public_project.lfs_objects << lfs_object
-          end
-
-          it 'responds with status 200 and error message' do
-            response = router.try_call
-            expect(response.first).to eq(200)
-            response_body = ActiveSupport::JSON.decode(response.last.first)
-
-            expect(response_body).to eq('objects' => [
-              { 'oid' => sample_oid,
-                'size' => sample_size,
-                'error' => {
-                  'code' => 404,
-                  'message' => "Object does not exist on the server or you don't have permissions to access it",
-                }
-              }])
-          end
-        end
-
-        context 'when downloading a lfs object that does not exist' do
-          before do
-            body = { 'operation' => 'download',
-                     'objects' => [
-                       { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
-                         'size' => 1575078
-                       }]
-            }.to_json
-            env['rack.input'] = StringIO.new(body)
-          end
-
-          it "responds with status 200 and error message" do
-            response = router.try_call
-            expect(response.first).to eq(200)
-            response_body = ActiveSupport::JSON.decode(response.last.first)
-
-            expect(response_body).to eq('objects' => [
-              { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
-                'size' => 1575078,
-                'error' => {
-                  'code' => 404,
-                  'message' => "Object does not exist on the server or you don't have permissions to access it",
-                }
-              }])
-          end
-        end
-
-        context 'when downloading one new and one existing lfs object' do
-          before do
-            body = { 'operation' => 'download',
-                     'objects' => [
-                       { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
-                         'size' => 1575078
-                       },
-                       { 'oid' => sample_oid,
-                         'size' => sample_size
-                       }
-                     ]
-            }.to_json
-            env['rack.input'] = StringIO.new(body)
-            project.lfs_objects << lfs_object
-          end
-
-          it "responds with status 200 with upload hypermedia link for the new object" do
-            response = router.try_call
-            expect(response.first).to eq(200)
-            response_body = ActiveSupport::JSON.decode(response.last.first)
-
-            expect(response_body).to eq('objects' => [
-              { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
-                'size' => 1575078,
-                'error' => {
-                  'code' => 404,
-                  'message' => "Object does not exist on the server or you don't have permissions to access it",
-                }
-              },
-              { 'oid' => sample_oid,
-                'size' => sample_size,
-                'actions' => {
-                  'download' => {
-                    'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
-                    'header' => { 'Authorization' => auth }
-                  }
-                }
-              }])
-          end
-        end
-      end
-
-      context 'when user is authenticated' do
-        let(:auth) { authorize(user) }
-
-        before do
-          env["HTTP_AUTHORIZATION"] = auth
-          project.team << [user, role]
-        end
-
-        it_behaves_like 'an authorized requests' do
-          let(:role) { :reporter }
-          let(:router) { lfs_router_auth }
-        end
-
-        context 'when user does is not member of the project' do
-          let(:role) { :guest }
-
-          it 'responds with 403' do
-            expect(lfs_router_auth.try_call.first).to eq(403)
-          end
-        end
-
-        context 'when user does not have download access' do
-          let(:role) { :guest }
-
-          it 'responds with 403' do
-            expect(lfs_router_auth.try_call.first).to eq(403)
-          end
-        end
-      end
-
-      context 'when CI is authorized' do
-        let(:auth) { 'gitlab-ci-token:password' }
-
-        before do
-          env["HTTP_AUTHORIZATION"] = auth
-        end
-
-        it_behaves_like 'an authorized requests' do
-          let(:router) { lfs_router_ci_auth }
-        end
-      end
-
-      context 'when user is not authenticated' do
-        describe 'is accessing public project' do
-          before do
-            public_project.lfs_objects << lfs_object
-          end
-
-          it 'responds with status 200 and href to download' do
-            response = lfs_router_public_noauth.try_call
-            expect(response.first).to eq(200)
-            response_body = ActiveSupport::JSON.decode(response.last.first)
-
-            expect(response_body).to eq('objects' => [
-              { 'oid' => sample_oid,
-                'size' => sample_size,
-                'actions' => {
-                  'download' => {
-                    'href' => "#{public_project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
-                    'header' => {}
-                  }
-                }
-              }])
-          end
-        end
-
-        describe 'is accessing non-public project' do
-          before do
-            project.lfs_objects << lfs_object
-          end
-
-          it 'responds with authorization required' do
-            expect(lfs_router_noauth.try_call.first).to eq(401)
-          end
-        end
-      end
-    end
-
-    describe 'upload' do
-      before do
-        body = { 'operation' => 'upload',
-                 'objects' => [
-                   { 'oid' => sample_oid,
-                     'size' => sample_size
-                   }]
-        }.to_json
-        env['rack.input'] = StringIO.new(body)
-      end
-
-      describe 'when request is authenticated' do
-        describe 'when user has project push access' do
-          before do
-            @auth = authorize(user)
-            env["HTTP_AUTHORIZATION"] = @auth
-            project.team << [user, :developer]
-          end
-
-          context 'when pushing an lfs object that already exists' do
-            before do
-              public_project.lfs_objects << lfs_object
-            end
-
-            it "responds with status 200 and links the object to the project" do
-              response_body = lfs_router_auth.try_call.last
-              response = ActiveSupport::JSON.decode(response_body.first)
-
-              expect(response['objects']).to be_kind_of(Array)
-              expect(response['objects'].first['oid']).to eq(sample_oid)
-              expect(response['objects'].first['size']).to eq(sample_size)
-              expect(lfs_object.projects.pluck(:id)).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)
-            end
-          end
-
-          context 'when pushing a lfs object that does not exist' do
-            before do
-              body = { 'operation' => 'upload',
-                       'objects' => [
-                         { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
-                           'size' => 1575078
-                         }]
-              }.to_json
-              env['rack.input'] = StringIO.new(body)
-            end
-
-            it "responds with status 200 and upload hypermedia link" do
-              response = lfs_router_auth.try_call
-              expect(response.first).to eq(200)
-
-              response_body = ActiveSupport::JSON.decode(response.last.first)
-              expect(response_body['objects']).to be_kind_of(Array)
-              expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
-              expect(response_body['objects'].first['size']).to eq(1575078)
-              expect(lfs_object.projects.pluck(:id)).not_to include(project.id)
-              expect(response_body['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
-              expect(response_body['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth)
-            end
-          end
-
-          context 'when pushing one new and one existing lfs object' do
-            before do
-              body = { 'operation' => 'upload',
-                       'objects' => [
-                         { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
-                           'size' => 1575078
-                         },
-                         { 'oid' => sample_oid,
-                           'size' => sample_size
-                         }
-                       ]
-              }.to_json
-              env['rack.input'] = StringIO.new(body)
-              project.lfs_objects << lfs_object
-            end
-
-            it "responds with status 200 with upload hypermedia link for the new object" do
-              response = lfs_router_auth.try_call
-              expect(response.first).to eq(200)
-
-              response_body = ActiveSupport::JSON.decode(response.last.first)
-              expect(response_body['objects']).to be_kind_of(Array)
-
-              expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
-              expect(response_body['objects'].first['size']).to eq(1575078)
-              expect(response_body['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
-              expect(response_body['objects'].first['actions']['upload']['header']).to eq("Authorization" => @auth)
-
-              expect(response_body['objects'].last['oid']).to eq(sample_oid)
-              expect(response_body['objects'].last['size']).to eq(sample_size)
-              expect(response_body['objects'].last).not_to have_key('actions')
-            end
-          end
-        end
-
-        context 'when user does not have push access' do
-          it 'responds with 403' do
-            expect(lfs_router_auth.try_call.first).to eq(403)
-          end
-        end
-
-        context 'when CI is authorized' do
-          it 'responds with 401' do
-            expect(lfs_router_ci_auth.try_call.first).to eq(401)
-          end
-        end
-      end
-
-      context 'when user is not authenticated' do
-        context 'when user has push access' do
-          before do
-            project.team << [user, :master]
-          end
-
-          it "responds with status 401" do
-            expect(lfs_router_public_noauth.try_call.first).to eq(401)
-          end
-        end
-
-        context 'when user does not have push access' do
-          it "responds with status 401" do
-            expect(lfs_router_public_noauth.try_call.first).to eq(401)
-          end
-        end
-      end
-
-      context 'when CI is authorized' do
-        let(:auth) { 'gitlab-ci-token:password' }
-
-        before do
-          env["HTTP_AUTHORIZATION"] = auth
-        end
-
-        it "responds with status 403" do
-          expect(lfs_router_public_ci_auth.try_call.first).to eq(401)
-        end
-      end
-    end
-
-    describe 'unsupported' do
-      before do
-        body = { 'operation' => 'other',
-                 'objects' => [
-                   { 'oid' => sample_oid,
-                     'size' => sample_size
-                   }]
-        }.to_json
-        env['rack.input'] = StringIO.new(body)
-      end
-
-      it 'responds with status 404' do
-        expect(lfs_router_public_noauth.try_call.first).to eq(404)
-      end
-    end
-  end
-
-  describe 'when pushing a lfs object' do
-    before do
-      enable_lfs
-      env['REQUEST_METHOD'] = 'PUT'
-    end
-
-    shared_examples 'unauthorized' do
-      context 'and request is sent by gitlab-workhorse to authorize the request' do
-        before do
-          header_for_upload_authorize(router.project)
-        end
-
-        it 'responds with status 401' do
-          expect(router.try_call.first).to eq(401)
-        end
-      end
-
-      context 'and request is sent by gitlab-workhorse to finalize the upload' do
-        before do
-          headers_for_upload_finalize(router.project)
-        end
-
-        it 'responds with status 401' do
-          expect(router.try_call.first).to eq(401)
-        end
-      end
-
-      context 'and request is sent with a malformed headers' do
-        before do
-          env["PATH_INFO"] = "#{router.project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}"
-          env["HTTP_X_GITLAB_LFS_TMP"] = "cat /etc/passwd"
-        end
-
-        it 'does not recognize it as a valid lfs command' do
-          expect(router.try_call).to eq(nil)
-        end
-      end
-    end
-
-    shared_examples 'forbidden' do
-      context 'and request is sent by gitlab-workhorse to authorize the request' do
-        before do
-          header_for_upload_authorize(router.project)
-        end
-
-        it 'responds with 403' do
-          expect(router.try_call.first).to eq(403)
-        end
-      end
-
-      context 'and request is sent by gitlab-workhorse to finalize the upload' do
-        before do
-          headers_for_upload_finalize(router.project)
-        end
-
-        it 'responds with 403' do
-          expect(router.try_call.first).to eq(403)
-        end
-      end
-    end
-
-    describe 'to one project' do
-      describe 'when user is authenticated' do
-        describe 'when user has push access to the project' do
-          before do
-            project.team << [user, :developer]
-          end
-
-          context 'and request is sent by gitlab-workhorse to authorize the request' do
-            before do
-              header_for_upload_authorize(project)
-            end
-
-            it 'responds with status 200, location of lfs store and object details' do
-              json_response = ActiveSupport::JSON.decode(lfs_router_auth.try_call.last.first)
-
-              expect(lfs_router_auth.try_call.first).to eq(200)
-              expect(json_response['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
-              expect(json_response['LfsOid']).to eq(sample_oid)
-              expect(json_response['LfsSize']).to eq(sample_size)
-            end
-          end
-
-          context 'and request is sent by gitlab-workhorse to finalize the upload' do
-            before do
-              headers_for_upload_finalize(project)
-            end
-
-            it 'responds with status 200 and lfs object is linked to the project' do
-              expect(lfs_router_auth.try_call.first).to eq(200)
-              expect(lfs_object.projects.pluck(:id)).to include(project.id)
-            end
-          end
-        end
-
-        describe 'and user does not have push access' do
-          let(:router) { lfs_router_auth }
-
-          it_behaves_like 'forbidden'
-        end
-      end
-
-      context 'when CI is authenticated' do
-        let(:router) { lfs_router_ci_auth }
-
-        it_behaves_like 'unauthorized'
-      end
-
-      context 'for unauthenticated' do
-        let(:router) { new_lfs_router(project) }
-
-        it_behaves_like 'unauthorized'
-      end
-    end
-
-    describe 'to a forked project' do
-      let(:forked_project) { fork_project(public_project, user) }
-
-      describe 'when user is authenticated' do
-        describe 'when user has push access to the project' do
-          before do
-            forked_project.team << [user_two, :developer]
-          end
-
-          context 'and request is sent by gitlab-workhorse to authorize the request' do
-            before do
-              header_for_upload_authorize(forked_project)
-            end
-
-            it 'responds with status 200, location of lfs store and object details' do
-              json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first)
-
-              expect(lfs_router_forked_auth.try_call.first).to eq(200)
-              expect(json_response['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
-              expect(json_response['LfsOid']).to eq(sample_oid)
-              expect(json_response['LfsSize']).to eq(sample_size)
-            end
-          end
-
-          context 'and request is sent by gitlab-workhorse to finalize the upload' do
-            before do
-              headers_for_upload_finalize(forked_project)
-            end
-
-            it 'responds with status 200 and lfs object is linked to the source project' do
-              expect(lfs_router_forked_auth.try_call.first).to eq(200)
-              expect(lfs_object.projects.pluck(:id)).to include(public_project.id)
-            end
-          end
-        end
-
-        describe 'and user does not have push access' do
-          let(:router) { lfs_router_forked_auth }
-
-          it_behaves_like 'forbidden'
-        end
-      end
-
-      context 'when CI is authenticated' do
-        let(:router) { lfs_router_forked_ci_auth }
-
-        it_behaves_like 'unauthorized'
-      end
-
-      context 'for unauthenticated' do
-        let(:router) { lfs_router_forked_noauth }
-
-        it_behaves_like 'unauthorized'
-      end
-
-      describe 'and second project not related to fork or a source project' do
-        let(:second_project) { create(:project) }
-        let(:lfs_router_second_project) { new_lfs_router(second_project, user: user) }
-
-        before do
-          public_project.lfs_objects << lfs_object
-          headers_for_upload_finalize(second_project)
-        end
-
-        context 'when pushing the same lfs object to the second project' do
-          before do
-            second_project.team << [user, :master]
-          end
-
-          it 'responds with 200 and links the lfs object to the project' do
-            expect(lfs_router_second_project.try_call.first).to eq(200)
-            expect(lfs_object.projects.pluck(:id)).to include(second_project.id, public_project.id)
-          end
-        end
-      end
-    end
-  end
-
-  def enable_lfs
-    allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
-  end
-
-  def authorize(user)
-    ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
-  end
-
-  def new_lfs_router(project, user: nil, ci: false)
-    Gitlab::Lfs::Router.new(project, user, ci, request)
-  end
-
-  def header_for_upload_authorize(project)
-    env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}/authorize"
-  end
-
-  def headers_for_upload_finalize(project)
-    env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}"
-    env["HTTP_X_GITLAB_LFS_TMP"] = "#{sample_oid}6e561c9d4"
-  end
-
-  def fork_project(project, user, object = nil)
-    allow(RepositoryForkWorker).to receive(:perform_async).and_return(true)
-    Projects::ForkService.new(project, user, {}).execute
-  end
-end
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index 8a8a4b46f0874b0ec2d5aca3c705d93e8931ba03..d435cd745b37aaecc00fa1f2c76b0e4882f1598b 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -210,7 +210,7 @@ describe Ci::Build, models: true do
       end
 
       before do
-        build.update_attributes(stage: 'stage')
+        build.update_attributes(stage: 'stage', yaml_variables: yaml_variables)
       end
 
       it { is_expected.to eq(predefined_variables + yaml_variables) }
@@ -262,22 +262,6 @@ describe Ci::Build, models: true do
 
           it { is_expected.to eq(predefined_variables + predefined_trigger_variable + yaml_variables + secure_variables + trigger_variables) }
         end
-
-        context 'when job variables are defined' do
-          ##
-          # Job-level variables are defined in gitlab_ci.yml fixture
-          #
-          context 'when job variables are unique' do
-            let(:build) { create(:ci_build, name: 'staging') }
-
-            it 'includes job variables' do
-              expect(subject).to include(
-                { key: :KEY1, value: 'value1', public: true },
-                { key: :KEY2, value: 'value2', public: true }
-              )
-            end
-          end
-        end
       end
     end
   end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 34507cf508323b9ce13b95cbfd5bbf14b026d31b..10db79bd15fd49d06de9474bb1962b520ea81cab 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -5,9 +5,12 @@ describe Ci::Pipeline, models: true do
   let(:pipeline) { FactoryGirl.create :ci_pipeline, project: project }
 
   it { is_expected.to belong_to(:project) }
+  it { is_expected.to belong_to(:user) }
+
   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 }
 
@@ -15,7 +18,7 @@ describe Ci::Pipeline, models: true do
   it { is_expected.to respond_to :git_author_email }
   it { is_expected.to respond_to :short_sha }
 
-  describe :valid_commit_sha do
+  describe '#valid_commit_sha' do
     context 'commit.sha can not start with 00000000' do
       before do
         pipeline.sha = '0' * 40
@@ -26,7 +29,7 @@ describe Ci::Pipeline, models: true do
     end
   end
 
-  describe :short_sha do
+  describe '#short_sha' do
     subject { pipeline.short_sha }
 
     it 'has 8 items' do
@@ -35,10 +38,10 @@ describe Ci::Pipeline, models: true do
     it { expect(pipeline.sha).to start_with(subject) }
   end
 
-  describe :create_next_builds do
+  describe '#create_next_builds' do
   end
 
-  describe :retried do
+  describe '#retried' do
     subject { pipeline.retried }
 
     before do
@@ -51,7 +54,7 @@ describe Ci::Pipeline, models: true do
     end
   end
 
-  describe :create_builds do
+  describe '#create_builds' do
     let!(:pipeline) { FactoryGirl.create :ci_pipeline, project: project, ref: 'master', tag: false }
 
     def create_builds(trigger_request = nil)
diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb
index 98f60087cf5e957db2c795a7117255b01bf6f2a4..4e7833c31623f31ed37a5f8503735047c5f3394e 100644
--- a/spec/models/ci/variable_spec.rb
+++ b/spec/models/ci/variable_spec.rb
@@ -9,7 +9,7 @@ describe Ci::Variable, models: true do
     subject.value = secret_value
   end
 
-  describe :value do
+  describe '#value' do
     it 'stores the encrypted value' do
       expect(subject.encrypted_value).not_to be_nil
     end
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index 96397d7c8a9cf10852ec7f1ac49b8772f337f775..ff6371ad68540677ac639f1090d4bbeb8dc9935a 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -24,14 +24,14 @@ describe CommitStatus, models: true do
   it { is_expected.to respond_to :running? }
   it { is_expected.to respond_to :pending? }
 
-  describe :author do
+  describe '#author' do
     subject { commit_status.author }
     before { commit_status.author = User.new }
 
     it { is_expected.to eq(commit_status.user) }
   end
 
-  describe :started? do
+  describe '#started?' do
     subject { commit_status.started? }
 
     context 'without started_at' do
@@ -57,7 +57,7 @@ describe CommitStatus, models: true do
     end
   end
 
-  describe :active? do
+  describe '#active?' do
     subject { commit_status.active? }
 
     %w(pending running).each do |state|
@@ -77,7 +77,7 @@ describe CommitStatus, models: true do
     end
   end
 
-  describe :complete? do
+  describe '#complete?' do
     subject { commit_status.complete? }
 
     %w(success failed canceled).each do |state|
@@ -97,7 +97,7 @@ describe CommitStatus, models: true do
     end
   end
 
-  describe :duration do
+  describe '#duration' do
     subject { commit_status.duration }
 
     it { is_expected.to eq(120.0) }
@@ -122,7 +122,7 @@ describe CommitStatus, models: true do
     end
   end
 
-  describe :latest do
+  describe '.latest' do
     subject { CommitStatus.latest.order(:id) }
 
     before do
@@ -138,7 +138,7 @@ describe CommitStatus, models: true do
     end
   end
 
-  describe :running_or_pending do
+  describe '.running_or_pending' do
     subject { CommitStatus.running_or_pending.order(:id) }
 
     before do
@@ -177,10 +177,10 @@ describe CommitStatus, models: true do
 
   describe '#stages' do
     before do
-      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'
+      create :commit_status, pipeline: pipeline, stage: 'build', name: 'linux', stage_idx: 0, status: 'success'
+      create :commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'failed'
+      create :commit_status, pipeline: pipeline, stage: 'deploy', name: 'staging', stage_idx: 2, status: 'running'
+      create :commit_status, pipeline: pipeline, stage: 'test', name: 'rspec', stage_idx: 1, status: 'success'
     end
 
     context 'stages list' do
@@ -192,7 +192,7 @@ describe CommitStatus, models: true do
     end
 
     context 'stages with statuses' do
-      subject { CommitStatus.where(pipeline: pipeline).stages_status }
+      subject { CommitStatus.where(pipeline: pipeline).latest.stages_status }
 
       it 'return list of stages with statuses' do
         is_expected.to eq({
@@ -201,6 +201,20 @@ describe CommitStatus, models: true do
           'deploy' => 'running'
         })
       end
+
+      context 'when build is retried' do
+        before do
+          create :commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'success'
+        end
+
+        it 'ignores a previous state' do
+          is_expected.to eq({
+            'build' => 'success',
+            'test' => 'success',
+            'deploy' => 'running'
+          })
+        end
+      end
     end
   end
 
diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb
index 0344dae8b5d13e40238115bedb4e3445d24eec9c..5e652660e2c4cd3db1e898f75563e4bda939e5ac 100644
--- a/spec/models/concerns/mentionable_spec.rb
+++ b/spec/models/concerns/mentionable_spec.rb
@@ -7,7 +7,7 @@ describe Mentionable do
     nil
   end
 
-  describe :references do
+  describe 'references' do
     let(:project) { create(:project) }
 
     it 'excludes JIRA references' do
diff --git a/spec/models/forked_project_link_spec.rb b/spec/models/forked_project_link_spec.rb
index fa1a0d4e0c77f37960cb3394f61634ec9dafc9fc..f94987dcaff81ad42f78fb7878d3bf16f36dfbb8 100644
--- a/spec/models/forked_project_link_spec.rb
+++ b/spec/models/forked_project_link_spec.rb
@@ -18,7 +18,7 @@ describe ForkedProjectLink, "add link on fork" do
   end
 end
 
-describe :forked_from_project do
+describe '#forked?' do
   let(:forked_project_link) { build(:forked_project_link) }
   let(:project_from) { create(:project) }
   let(:project_to) { create(:project, forked_project_link: forked_project_link) }
diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb
index c4e781dd1dcaba14d3a230c382a3ade2aab264ed..615cfe3142b1b78dc8adc9c178d635a03cc61b09 100644
--- a/spec/models/generic_commit_status_spec.rb
+++ b/spec/models/generic_commit_status_spec.rb
@@ -4,33 +4,33 @@ describe GenericCommitStatus, models: true do
   let(:pipeline) { FactoryGirl.create :ci_pipeline }
   let(:generic_commit_status) { FactoryGirl.create :generic_commit_status, pipeline: pipeline }
 
-  describe :context do
+  describe '#context' do
     subject { generic_commit_status.context }
     before { generic_commit_status.context = 'my_context' }
 
     it { is_expected.to eq(generic_commit_status.name) }
   end
 
-  describe :tags do
+  describe '#tags' do
     subject { generic_commit_status.tags }
 
     it { is_expected.to eq([:external]) }
   end
 
-  describe :set_default_values do
+  describe 'set_default_values' do
     before do
       generic_commit_status.context = nil
       generic_commit_status.stage = nil
       generic_commit_status.save
     end
 
-    describe :context do
+    describe '#context' do
       subject { generic_commit_status.context }
 
       it { is_expected.not_to be_nil }
     end
 
-    describe :stage do
+    describe '#stage' do
       subject { generic_commit_status.stage }
 
       it { is_expected.not_to be_nil }
diff --git a/spec/models/global_milestone_spec.rb b/spec/models/global_milestone_spec.rb
index 197c99cd007ea9c80f30ef2474210b85d1d57907..ae77ec5b3489e2064c93940559a24142a2197321 100644
--- a/spec/models/global_milestone_spec.rb
+++ b/spec/models/global_milestone_spec.rb
@@ -14,7 +14,7 @@ describe GlobalMilestone, models: true do
   let(:milestone2_project2) { create(:milestone, title: "VD-123", project: project2) }
   let(:milestone2_project3) { create(:milestone, title: "VD-123", project: project3) }
 
-  describe :build_collection do
+  describe '.build_collection' do
     before do
       milestones =
         [
@@ -42,7 +42,7 @@ describe GlobalMilestone, models: true do
     end
   end
 
-  describe :initialize do
+  describe '#initialize' do
     before do
       milestones =
         [
@@ -63,7 +63,7 @@ describe GlobalMilestone, models: true do
     end
   end
 
-  describe :safe_title do
+  describe '#safe_title' do
     let(:milestone) { create(:milestone, title: "git / test", project: project1) }
 
     it 'should strip out slashes and spaces' do
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index a878ff1b2278536d2f3b88ad2887ef562eb23103..266c46213a695645246134f271861e4226571317 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -97,22 +97,22 @@ describe Group, models: true do
     end
   end
 
-  describe :users do
+  describe '#users' do
     it { expect(group.users).to eq(group.owners) }
   end
 
-  describe :human_name do
+  describe '#human_name' do
     it { expect(group.human_name).to eq(group.name) }
   end
 
-  describe :add_users do
+  describe '#add_user' do
     let(:user) { create(:user) }
     before { group.add_user(user, GroupMember::MASTER) }
 
     it { expect(group.group_members.masters.map(&:user)).to include(user) }
   end
 
-  describe :add_users do
+  describe '#add_users' do
     let(:user) { create(:user) }
     before { group.add_users([user.id], GroupMember::GUEST) }
 
@@ -124,7 +124,7 @@ describe Group, models: true do
     end
   end
 
-  describe :avatar_type do
+  describe '#avatar_type' do
     let(:user) { create(:user) }
     before { group.add_user(user, GroupMember::MASTER) }
 
diff --git a/spec/models/legacy_diff_note_spec.rb b/spec/models/legacy_diff_note_spec.rb
index d64d89edbd3213395c0d5b28e3aab8a9e7c5d7b6..d23fc06c3adf163f54e5978a1d5760a81a40b7e3 100644
--- a/spec/models/legacy_diff_note_spec.rb
+++ b/spec/models/legacy_diff_note_spec.rb
@@ -16,10 +16,10 @@ describe LegacyDiffNote, models: true do
   end
 
   describe '#active?' do
-    it 'is always true when the note has no associated diff' do
+    it 'is always true when the note has no associated diff line' do
       note = build(:legacy_diff_note_on_merge_request)
 
-      expect(note).to receive(:diff).and_return(nil)
+      expect(note).to receive(:diff_line).and_return(nil)
 
       expect(note).to be_active
     end
@@ -27,7 +27,7 @@ describe LegacyDiffNote, models: true do
     it 'is never true when the note has no noteable associated' do
       note = build(:legacy_diff_note_on_merge_request)
 
-      expect(note).to receive(:diff).and_return(double)
+      expect(note).to receive(:diff_line).and_return(double)
       expect(note).to receive(:noteable).and_return(nil)
 
       expect(note).not_to be_active
@@ -47,7 +47,7 @@ describe LegacyDiffNote, models: true do
         merge = build_stubbed(:merge_request, :simple)
         note = build(:legacy_diff_note_on_merge_request, noteable: merge)
 
-        allow(note).to receive(:diff).and_return(double)
+        allow(note).to receive(:diff_line).and_return(double)
         expect(note).to receive(:find_noteable_diff).and_return(nil)
 
         expect(note).not_to be_active
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index 4c10346243357e085a9f7b4a2f9fa69d023fe1d5..ba622dfb9becdc4309d22872e8b851333a36bd8e 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -101,7 +101,7 @@ describe ProjectMember, models: true do
     end
   end
 
-  describe :add_users_into_projects do
+  describe '.add_users_into_projects' do
     before do
       @project_1 = create :project
       @project_2 = create :project
@@ -123,7 +123,7 @@ describe ProjectMember, models: true do
     it { expect(@project_2.users).to include(@user_2) }
   end
 
-  describe :truncate_teams do
+  describe '.truncate_teams' do
     before do
       @project_1 = create :project
       @project_2 = create :project
diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9a637c94fbe11599322034bb2121cb0e029d3908
--- /dev/null
+++ b/spec/models/merge_request_diff_spec.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+
+describe MergeRequestDiff, models: true do
+  describe '#diffs' do
+    let(:mr) { create(:merge_request, :with_diffs) }
+    let(:mr_diff) { mr.merge_request_diff }
+
+    context 'when the :ignore_whitespace_change option is set' do
+      it 'creates a new compare object instead of loading from the DB' do
+        expect(mr_diff).not_to receive(:load_diffs)
+        expect(Gitlab::Git::Compare).to receive(:new).and_call_original
+
+        mr_diff.diffs(ignore_whitespace_change: true)
+      end
+    end
+
+    context 'when the raw diffs are empty' do
+      before { mr_diff.update_attributes(st_diffs: '') }
+
+      it 'returns an empty DiffCollection' do
+        expect(mr_diff.diffs).to be_a(Gitlab::Git::DiffCollection)
+        expect(mr_diff.diffs).to be_empty
+      end
+    end
+
+    context 'when the raw diffs exist' do
+      it 'returns the diffs' do
+        expect(mr_diff.diffs).to be_a(Gitlab::Git::DiffCollection)
+        expect(mr_diff.diffs).not_to be_empty
+      end
+
+      context 'when the :paths option is set' do
+        let(:diffs) { mr_diff.diffs(paths: ['files/ruby/popen.rb', 'files/ruby/popen.rb']) }
+
+        it 'only returns diffs that match the (old path, new path) given' do
+          expect(diffs.map(&:new_path)).to contain_exactly('files/ruby/popen.rb')
+        end
+
+        it 'uses the diffs from the DB' do
+          expect(mr_diff).to receive(:load_diffs)
+
+          diffs
+        end
+      end
+    end
+  end
+end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index a4b6ff8f8ad501e3eff4efe3d8afed49cd2affd3..c8ad7ab3e7f59ed483dcacf3f4954496eab4cce0 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -116,6 +116,31 @@ describe MergeRequest, models: true do
     end
   end
 
+  describe '#diffs' do
+    let(:merge_request) { build(:merge_request) }
+    let(:options) { { paths: ['a/b', 'b/a', 'c/*'] } }
+
+    context 'when there are MR diffs' do
+      it 'delegates to the MR diffs' do
+        merge_request.merge_request_diff = MergeRequestDiff.new
+
+        expect(merge_request.merge_request_diff).to receive(:diffs).with(options)
+
+        merge_request.diffs(options)
+      end
+    end
+
+    context 'when there are no MR diffs' do
+      it 'delegates to the compare object' do
+        merge_request.compare = double(:compare)
+
+        expect(merge_request.compare).to receive(:diffs).with(options)
+
+        merge_request.diffs(options)
+      end
+    end
+  end
+
   describe "#mr_and_commit_notes" do
     let!(:merge_request) { create(:merge_request) }
 
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index 1e18c788b503d0c48f306bda2b05cb02fb9fb285..d661dc0e59ab1067486b1f31c3a37f1fe45b1da8 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -70,7 +70,7 @@ describe Milestone, models: true do
     end
   end
 
-  describe :expired? do
+  describe '#expired?' do
     context "expired" do
       before do
         allow(milestone).to receive(:due_date).and_return(Date.today.prev_year)
@@ -88,7 +88,7 @@ describe Milestone, models: true do
     end
   end
 
-  describe :percent_complete do
+  describe '#percent_complete' do
     before do
       allow(milestone).to receive_messages(
         closed_items_count: 3,
@@ -111,11 +111,11 @@ describe Milestone, models: true do
     it { expect(milestone.is_empty?(user)).to be_falsey }
   end
 
-  describe :can_be_closed? do
+  describe '#can_be_closed?' do
     it { expect(milestone.can_be_closed?).to be_truthy }
   end
 
-  describe :total_items_count do
+  describe '#total_items_count' do
     before do
       create :closed_issue, milestone: milestone
       create :merge_request, milestone: milestone
@@ -126,7 +126,7 @@ describe Milestone, models: true do
     end
   end
 
-  describe :can_be_closed? do
+  describe '#can_be_closed?' do
     before do
       milestone = create :milestone
       create :closed_issue, milestone: milestone
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 5f68cd2b066925bdab9d0151b47fb63164cc3695..a162da0208e96a10ce1627c933875d0d4e0ce028 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -18,11 +18,11 @@ describe Namespace, models: true do
     it { is_expected.to respond_to(:to_param) }
   end
 
-  describe :to_param do
+  describe '#to_param' do
     it { expect(namespace.to_param).to eq(namespace.path) }
   end
 
-  describe :human_name do
+  describe '#human_name' do
     it { expect(namespace.human_name).to eq(namespace.owner_name) }
   end
 
@@ -54,7 +54,7 @@ describe Namespace, models: true do
     end
   end
 
-  describe :move_dir do
+  describe '#move_dir' do
     before do
       @namespace = create :namespace
       @project = create :project, namespace: @namespace
@@ -98,7 +98,7 @@ describe Namespace, models: true do
     end
   end
 
-  describe :find_by_path_or_name do
+  describe '.find_by_path_or_name' do
     before do
       @namespace = create(:namespace, name: 'WoW', path: 'woW')
     end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 6549791f6758de24d493f919fb2d505f859d8ab4..7d0697dab424475f824838785d38df2bf8e1609c 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -226,6 +226,20 @@ describe Note, models: true do
     it "returns false" do
       expect(note.cross_reference_not_visible_for?(private_user)).to be_falsy
     end
+
+    it "returns false if user visible reference count set" do
+      note.user_visible_reference_count = 1
+
+      expect(note).not_to receive(:reference_mentionables)
+      expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_falsy
+    end
+
+    it "returns true if ref count is 0" do
+      note.user_visible_reference_count = 0
+
+      expect(note).not_to receive(:reference_mentionables)
+      expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
+    end
   end
 
   describe 'clear_blank_line_code!' do
diff --git a/spec/models/project_security_spec.rb b/spec/models/project_security_spec.rb
index e12258c0874200f7ca0cf71d183d3ac7cc4ed711..2142c7c13ef9fa5587f324eb33100ed862bf9a83 100644
--- a/spec/models/project_security_spec.rb
+++ b/spec/models/project_security_spec.rb
@@ -1,7 +1,7 @@
 require 'spec_helper'
 
 describe Project, models: true do
-  describe :authorization do
+  describe 'authorization' do
     before do
       @p1 = create(:project)
 
diff --git a/spec/models/project_services/buildkite_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb
index 60364df20154469aabebe1d5a036330c5a3c9647..0866e1532dd2b03a9469a411ecc1792315662369 100644
--- a/spec/models/project_services/buildkite_service_spec.rb
+++ b/spec/models/project_services/buildkite_service_spec.rb
@@ -57,7 +57,7 @@ describe BuildkiteService, models: true do
       )
     end
 
-    describe :webhook_url do
+    describe '#webhook_url' do
       it 'returns the webhook url' do
         expect(@service.webhook_url).to eq(
           'https://webhook.buildkite.com/deliver/secret-sauce-webhook-token'
@@ -65,7 +65,7 @@ describe BuildkiteService, models: true do
       end
     end
 
-    describe :commit_status_path do
+    describe '#commit_status_path' do
       it 'returns the correct status page' do
         expect(@service.commit_status_path('2ab7834c')).to eq(
           'https://gitlab.buildkite.com/status/secret-sauce-status-token.json?commit=2ab7834c'
@@ -73,7 +73,7 @@ describe BuildkiteService, models: true do
       end
     end
 
-    describe :build_page do
+    describe '#build_page' do
       it 'returns the correct build page' do
         expect(@service.build_page('2ab7834c', nil)).to eq(
           'https://buildkite.com/account-name/example-project/builds?commit=2ab7834c'
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 143fd5167a4ff8890276adaf9a7c7a5303cf3f74..53b420d808fa8bdc59df0d51f29f4298e2f198f2 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -130,17 +130,35 @@ describe Project, models: true do
       end
     end
 
-    it 'should not allow an invalid URI as import_url' do
+    it 'does not allow an invalid URI as import_url' do
       project2 = build(:project, import_url: 'invalid://')
 
       expect(project2).not_to be_valid
     end
 
-    it 'should allow a valid URI as import_url' do
+    it 'does allow a valid URI as import_url' do
       project2 = build(:project, import_url: 'ssh://test@gitlab.com/project.git')
 
       expect(project2).to be_valid
     end
+
+    it 'allows an empty URI' do
+      project2 = build(:project, import_url: '')
+
+      expect(project2).to be_valid
+    end
+
+    it 'does not produce import data on an empty URI' do
+      project2 = build(:project, import_url: '')
+
+      expect(project2.import_data).to be_nil
+    end
+
+    it 'does not produce import data on an invalid URI' do
+      project2 = build(:project, import_url: 'test://')
+
+      expect(project2.import_data).to be_nil
+    end
   end
 
   describe 'default_scope' do
@@ -296,7 +314,7 @@ describe Project, models: true do
     end
   end
 
-  describe :update_merge_requests do
+  describe '#update_merge_requests' do
     let(:project) { create(:project) }
     let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
     let(:key) { create(:key, user_id: project.owner.id) }
@@ -345,7 +363,7 @@ describe Project, models: true do
     end
   end
 
-  describe :to_param do
+  describe '#to_param' do
     context 'with namespace' do
       before do
         @group = create :group, name: 'gitlab'
@@ -356,7 +374,7 @@ describe Project, models: true do
     end
   end
 
-  describe :repository do
+  describe '#repository' do
     let(:project) { create(:project) }
 
     it 'returns valid repo' do
@@ -364,7 +382,7 @@ describe Project, models: true do
     end
   end
 
-  describe :default_issues_tracker? do
+  describe '#default_issues_tracker?' do
     let(:project) { create(:project) }
     let(:ext_project) { create(:redmine_project) }
 
@@ -377,7 +395,7 @@ describe Project, models: true do
     end
   end
 
-  describe :external_issue_tracker do
+  describe '#external_issue_tracker' do
     let(:project) { create(:project) }
     let(:ext_project) { create(:redmine_project) }
 
@@ -418,7 +436,7 @@ describe Project, models: true do
     end
   end
 
-  describe :cache_has_external_issue_tracker do
+  describe '#cache_has_external_issue_tracker' do
     let(:project) { create(:project) }
 
     it 'stores true if there is any external_issue_tracker' do
@@ -440,7 +458,7 @@ describe Project, models: true do
     end
   end
 
-  describe :open_branches do
+  describe '#open_branches' do
     let(:project) { create(:project) }
 
     before do
@@ -517,7 +535,7 @@ describe Project, models: true do
     end
   end
 
-  describe :avatar_type do
+  describe '#avatar_type' do
     let(:project) { create(:project) }
 
     it 'should be true if avatar is image' do
@@ -531,7 +549,7 @@ describe Project, models: true do
     end
   end
 
-  describe :avatar_url do
+  describe '#avatar_url' do
     subject { project.avatar_url }
 
     let(:project) { create(:project) }
@@ -568,7 +586,7 @@ describe Project, models: true do
     end
   end
 
-  describe :pipeline do
+  describe '#pipeline' do
     let(:project) { create :project }
     let(:pipeline) { create :ci_pipeline, project: project, ref: 'master' }
 
@@ -588,7 +606,7 @@ describe Project, models: true do
     end
   end
 
-  describe :builds_enabled do
+  describe '#builds_enabled' do
     let(:project) { create :project }
 
     before { project.builds_enabled = true }
@@ -690,7 +708,7 @@ describe Project, models: true do
     end
   end
 
-  describe :any_runners do
+  describe '#any_runners' do
     let(:project) { create(:empty_project, shared_runners_enabled: shared_runners_enabled) }
     let(:specific_runner) { create(:ci_runner) }
     let(:shared_runner) { create(:ci_runner, :shared) }
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 24e49c8def344c24db97e0bf1b175f8bcbc9ea5f..b39b958450c0ef17d9375824bc846f6ad024d993 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -16,7 +16,7 @@ describe Repository, models: true do
     repository.commit(merge_commit_sha)
   end
 
-  describe :branch_names_contains do
+  describe '#branch_names_contains' do
     subject { repository.branch_names_contains(sample_commit.id) }
 
     it { is_expected.to include('master') }
@@ -24,7 +24,7 @@ describe Repository, models: true do
     it { is_expected.not_to include('fix') }
   end
 
-  describe :tag_names_contains do
+  describe '#tag_names_contains' do
     subject { repository.tag_names_contains(sample_commit.id) }
 
     it { is_expected.to include('v1.1.0') }
@@ -72,13 +72,13 @@ describe Repository, models: true do
     end
   end
 
-  describe :last_commit_for_path do
+  describe '#last_commit_for_path' do
     subject { repository.last_commit_for_path(sample_commit.id, '.gitignore').id }
 
     it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') }
   end
 
-  describe :find_commits_by_message do
+  describe '#find_commits_by_message' do
     subject { repository.find_commits_by_message('submodule').map{ |k| k.id } }
 
     it { is_expected.to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
@@ -87,7 +87,7 @@ describe Repository, models: true do
     it { is_expected.not_to include('913c66a37b4a45b9769037c55c2d238bd0942d2e') }
   end
 
-  describe :blob_at do
+  describe '#blob_at' do
     context 'blank sha' do
       subject { repository.blob_at(Gitlab::Git::BLANK_SHA, '.gitignore') }
 
@@ -95,7 +95,7 @@ describe Repository, models: true do
     end
   end
 
-  describe :merged_to_root_ref? do
+  describe '#merged_to_root_ref?' do
     context 'merged branch' do
       subject { repository.merged_to_root_ref?('improve/awesome') }
 
@@ -103,7 +103,7 @@ describe Repository, models: true do
     end
   end
 
-  describe :can_be_merged? do
+  describe '#can_be_merged?' do
     context 'mergeable branches' do
       subject { repository.can_be_merged?('0b4bc9a49b562e85de7cc9e834518ea6828729b9', 'master') }
 
@@ -305,7 +305,7 @@ describe Repository, models: true do
     end
   end
 
-  describe :add_branch do
+  describe '#add_branch' do
     context 'when pre hooks were successful' do
       it 'should run without errors' do
         hook = double(trigger: [true, nil])
@@ -349,7 +349,7 @@ describe Repository, models: true do
     end
   end
 
-  describe :rm_branch do
+  describe '#rm_branch' do
     context 'when pre hooks were successful' do
       it 'should run without errors' do
         allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil])
@@ -386,7 +386,7 @@ describe Repository, models: true do
     end
   end
 
-  describe :commit_with_hooks do
+  describe '#commit_with_hooks' do
     context 'when pre hooks were successful' do
       before do
         expect_any_instance_of(GitHooksService).to receive(:execute).
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index 96bbbec9ea11be23879cf285454b8a2c70459294..67b3783d5145839b1c12694658059b9da99b9622 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -22,11 +22,11 @@ describe Service, models: true do
         @testable = @service.can_test?
       end
 
-      describe :can_test do
+      describe '#can_test?' do
         it { expect(@testable).to eq(true) }
       end
 
-      describe :test do
+      describe '#test' do
         let(:data) { 'test' }
 
         it 'test runs execute' do
@@ -45,7 +45,7 @@ describe Service, models: true do
         @testable = @service.can_test?
       end
 
-      describe :can_test do
+      describe '#can_test?' do
         it { expect(@testable).to eq(true) }
       end
     end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 3984b30ddf8a11f5df51fc4633537d7c3740bf0b..fc74488ac0e0a591a5a86b107f055618de2a01c1 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -31,6 +31,8 @@ describe User, models: true do
     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(:builds).dependent(:nullify) }
+    it { is_expected.to have_many(:pipelines).dependent(:nullify) }
 
     describe '#group_members' do
       it 'does not include group memberships for which user is a requester' do
@@ -427,7 +429,7 @@ describe User, models: true do
     end
   end
 
-  describe :not_in_project do
+  describe '.not_in_project' do
     before do
       User.delete_all
       @user = create :user
@@ -598,7 +600,7 @@ describe User, models: true do
     end
   end
 
-  describe :avatar_type do
+  describe '#avatar_type' do
     let(:user) { create(:user) }
 
     it "should be true if avatar is image" do
@@ -612,7 +614,7 @@ describe User, models: true do
     end
   end
 
-  describe :requires_ldap_check? do
+  describe '#requires_ldap_check?' do
     let(:user) { User.new }
 
     it 'is false when LDAP is disabled' do
@@ -651,7 +653,7 @@ describe User, models: true do
   end
 
   context 'ldap synchronized user' do
-    describe :ldap_user? do
+    describe '#ldap_user?' do
       it 'is true if provider name starts with ldap' do
         user = create(:omniauth_user, provider: 'ldapmain')
         expect(user.ldap_user?).to be_truthy
@@ -668,7 +670,7 @@ describe User, models: true do
       end
     end
 
-    describe :ldap_identity do
+    describe '#ldap_identity' do
       it 'returns ldap identity' do
         user = create :omniauth_user
         expect(user.ldap_identity.provider).not_to be_empty
@@ -825,7 +827,7 @@ describe User, models: true do
     end
   end
 
-  describe :can_be_removed? do
+  describe '#can_be_removed?' do
     subject { create(:user) }
 
     context 'no owned groups' do
diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb
index 72a6d45f47d92ae80a5052e059cd6d32ad67eb8c..2b74dd4bbb0c9c8d51cc7e40ba7c637098ce5664 100644
--- a/spec/requests/api/award_emoji_spec.rb
+++ b/spec/requests/api/award_emoji_spec.rb
@@ -135,6 +135,22 @@ describe API::API, api: true  do
 
         expect(response).to have_http_status(401)
       end
+
+      it "normalizes +1 as thumbsup award" do
+        post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user), name: '+1'
+
+        expect(issue.award_emoji.last.name).to eq("thumbsup")
+      end
+
+      context 'when the emoji already has been awarded' do
+        it 'returns a 404 status code' do
+          post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user), name: 'thumbsup'
+          post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user), name: 'thumbsup'
+
+          expect(response).to have_http_status(404)
+          expect(json_response["message"]).to match("has already been taken")
+        end
+      end
     end
   end
 
@@ -147,6 +163,22 @@ describe API::API, api: true  do
       expect(response).to have_http_status(201)
       expect(json_response['user']['username']).to eq(user.username)
     end
+
+    it "normalizes +1 as thumbsup award" do
+      post api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user), name: '+1'
+
+      expect(note.award_emoji.last.name).to eq("thumbsup")
+    end
+
+    context 'when the emoji already has been awarded' do
+      it 'returns a 404 status code' do
+        post api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user), name: 'rocket'
+        post api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user), name: 'rocket'
+
+        expect(response).to have_http_status(404)
+        expect(json_response["message"]).to match("has already been taken")
+      end
+    end
   end
 
   describe 'DELETE /projects/:id/awardable/:awardable_id/award_emoji/:award_id' do
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index e567d36afa8bf4472bc2efdcc55732d180235cb8..f6f85d6e95ede66a0d4badb985944e5560c23ba4 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -56,13 +56,21 @@ describe API::API, api: true  do
 
       context "git push with project.wiki" do
         it 'responds with success' do
-          project_wiki = create(:project, name: 'my.wiki', path: 'my.wiki')
-          project_wiki.team << [user, :developer]
+          push(key, project.wiki)
 
-          push(key, project_wiki)
+          expect(response).to have_http_status(200)
+          expect(json_response["status"]).to be_truthy
+          expect(json_response["repository_path"]).to eq(project.wiki.repository.path_to_repo)
+        end
+      end
+
+      context "git pull with project.wiki" do
+        it 'responds with success' do
+          pull(key, project.wiki)
 
           expect(response).to have_http_status(200)
           expect(json_response["status"]).to be_truthy
+          expect(json_response["repository_path"]).to eq(project.wiki.repository.path_to_repo)
         end
       end
 
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 6adccb4ebae9348f8d7e928b602a70b68267b2ef..12f2cfa69426d4dce431f72ebcd0a70792c4a0d1 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -503,6 +503,20 @@ describe API::API, api: true  do
       ])
     end
 
+    context 'with due date' do
+      it 'creates a new project issue' do
+        due_date = 2.weeks.from_now.strftime('%Y-%m-%d')
+
+        post api("/projects/#{project.id}/issues", user),
+          title: 'new issue', due_date: due_date
+
+        expect(response).to have_http_status(201)
+        expect(json_response['title']).to eq('new issue')
+        expect(json_response['description']).to be_nil
+        expect(json_response['due_date']).to eq(due_date)
+      end
+    end
+
     context 'when an admin or owner makes the request' do
       it 'accepts the creation date to be set' do
         creation_time = 2.weeks.ago
@@ -683,6 +697,17 @@ describe API::API, api: true  do
     end
   end
 
+  describe 'PUT /projects/:id/issues/:issue_id to update due date' do
+    it 'creates a new project issue' do
+      due_date = 2.weeks.from_now.strftime('%Y-%m-%d')
+
+      put api("/projects/#{project.id}/issues/#{issue.id}", user), due_date: due_date
+
+      expect(response).to have_http_status(200)
+      expect(json_response['due_date']).to eq(due_date)
+    end
+  end
+
   describe "DELETE /projects/:id/issues/:issue_id" do
     it "rejects a non member from deleting an issue" do
       delete api("/projects/#{project.id}/issues/#{issue.id}", non_member)
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 4a1b5600bdf3a27245a77bee7223991a1a5062dc..651b91e9f68a4d1e032dfdfb6e1d6f4346b16fa5 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -138,6 +138,8 @@ 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['should_close_merge_request']).to be_falsy
+      expect(json_response['force_close_merge_request']).to be_falsy
     end
 
     it "should return merge_request" do
@@ -147,6 +149,8 @@ describe API::API, api: true  do
       expect(json_response['iid']).to eq(merge_request.iid)
       expect(json_response['work_in_progress']).to eq(false)
       expect(json_response['merge_status']).to eq('can_be_merged')
+      expect(json_response['should_close_merge_request']).to be_falsy
+      expect(json_response['force_close_merge_request']).to be_falsy
     end
 
     it 'should return merge_request by iid' do
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 8a52725a89312d356b7545e46cc932e10ba49030..152cd8028391b231325a257d2cb291a740905ea5 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -81,6 +81,18 @@ describe API::API, api: true  do
         expect(json_response.first.keys).not_to include('open_issues_count')
       end
 
+      context 'GET /projects?simple=true' do
+        it 'returns a simplified version of all the projects' do
+          expected_keys = ["id", "http_url_to_repo", "web_url", "name", "name_with_namespace", "path", "path_with_namespace"]
+
+          get api('/projects?simple=true', user)
+
+          expect(response).to have_http_status(200)
+          expect(json_response).to be_an Array
+          expect(json_response.first.keys).to match_array expected_keys
+        end
+      end
+
       context 'and using search' do
         it 'should return searched project' do
           get api('/projects', user), { search: project.name }
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..93d2bc160cc8fc3ca3abcc52b3c773417a24c0b5
--- /dev/null
+++ b/spec/requests/lfs_http_spec.rb
@@ -0,0 +1,768 @@
+require 'spec_helper'
+
+describe Gitlab::Lfs::Router do
+  let(:user) { create(:user) }
+  let!(:lfs_object) { create(:lfs_object, :with_file) }
+
+  let(:headers) do
+    {
+      'Authorization' => authorization,
+      'X-Sendfile-Type' => sendfile
+    }.compact
+  end
+  let(:authorization) { }
+  let(:sendfile) { }
+
+  let(:sample_oid) { lfs_object.oid }
+  let(:sample_size) { lfs_object.size }
+
+  describe 'when lfs is disabled' do
+    let(:project) { create(:empty_project) }
+    let(:body) do
+      {
+        'objects' => [
+          { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+            'size' => 1575078
+          },
+          { 'oid' => sample_oid,
+            'size' => sample_size
+          }
+        ],
+        'operation' => 'upload'
+      }
+    end
+
+    before do
+      allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
+      post_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
+    end
+
+    it 'responds with 501' do
+      expect(response).to have_http_status(501)
+      expect(json_response).to include('message' => 'Git LFS is not enabled on this GitLab server, contact your admin.')
+    end
+  end
+
+  describe 'deprecated API' do
+    let(:project) { create(:empty_project) }
+
+    before do
+      enable_lfs
+    end
+
+    shared_examples 'a deprecated' do
+      it 'responds with 501' do
+        expect(response).to have_http_status(501)
+      end
+
+      it 'returns deprecated message' do
+        expect(json_response).to include('message' => 'Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.')
+      end
+    end
+
+    context 'when fetching lfs object using deprecated API' do
+      let(:authorization) { authorize_user }
+
+      before do
+        get "#{project.http_url_to_repo}/info/lfs/objects/#{sample_oid}", nil, headers
+      end
+
+      it_behaves_like 'a deprecated'
+    end
+
+    context 'when handling lfs request using deprecated API' do
+      before do
+        post_json "#{project.http_url_to_repo}/info/lfs/objects", nil, headers
+      end
+
+      it_behaves_like 'a deprecated'
+    end
+  end
+
+  describe 'when fetching lfs object' do
+    let(:project) { create(:empty_project) }
+    let(:update_permissions) { }
+
+    before do
+      enable_lfs
+      update_permissions
+      get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
+    end
+
+    context 'and request comes from gitlab-workhorse' do
+      context 'without user being authorized' do
+        it 'responds with status 401' do
+          expect(response).to have_http_status(401)
+        end
+      end
+
+      context 'with required headers' do
+        shared_examples 'responds with a file' do
+          let(:sendfile) { 'X-Sendfile' }
+
+          it 'responds with status 200' do
+            expect(response).to have_http_status(200)
+          end
+
+          it 'responds with the file location' do
+            expect(response.headers['Content-Type']).to eq('application/octet-stream')
+            expect(response.headers['X-Sendfile']).to eq(lfs_object.file.path)
+          end
+        end
+
+        context 'with user is authorized' do
+          let(:authorization) { authorize_user }
+
+          context 'and does not have project access' do
+            let(:update_permissions) do
+              project.lfs_objects << lfs_object
+            end
+
+            it 'responds with status 403' do
+              expect(response).to have_http_status(403)
+            end
+          end
+
+          context 'and does have project access' do
+            let(:update_permissions) do
+              project.team << [user, :master]
+              project.lfs_objects << lfs_object
+            end
+
+            it_behaves_like 'responds with a file'
+          end
+        end
+
+        context 'when CI is authorized' do
+          let(:authorization) { authorize_ci_project }
+
+          let(:update_permissions) do
+            project.lfs_objects << lfs_object
+          end
+
+          it_behaves_like 'responds with a file'
+        end
+      end
+
+      context 'without required headers' do
+        let(:authorization) { authorize_user }
+
+        it 'responds with status 403' do
+          expect(response).to have_http_status(403)
+        end
+      end
+    end
+  end
+
+  describe 'when handling lfs batch request' do
+    let(:update_lfs_permissions) { }
+    let(:update_user_permissions) { }
+
+    before do
+      enable_lfs
+      update_lfs_permissions
+      update_user_permissions
+      post_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
+    end
+
+    describe 'download' do
+      let(:project) { create(:empty_project) }
+      let(:body) do
+        { 'operation' => 'download',
+          'objects' => [
+            { 'oid' => sample_oid,
+              'size' => sample_size
+            }]
+        }
+      end
+
+      shared_examples 'an authorized requests' do
+        context 'when downloading an lfs object that is assigned to our project' do
+          let(:update_lfs_permissions) do
+            project.lfs_objects << lfs_object
+          end
+
+          it 'responds with status 200' do
+            expect(response).to have_http_status(200)
+          end
+
+          it 'with href to download' do
+            expect(json_response).to eq('objects' => [
+              { 'oid' => sample_oid,
+                'size' => sample_size,
+                'actions' => {
+                  'download' => {
+                    'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
+                    'header' => { 'Authorization' => authorization }
+                  }
+                }
+              }])
+          end
+        end
+
+        context 'when downloading an lfs object that is assigned to other project' do
+          let(:other_project) { create(:empty_project) }
+          let(:update_lfs_permissions) do
+            other_project.lfs_objects << lfs_object
+          end
+
+          it 'responds with status 200' do
+            expect(response).to have_http_status(200)
+          end
+
+          it 'with href to download' do
+            expect(json_response).to eq('objects' => [
+              { 'oid' => sample_oid,
+                'size' => sample_size,
+                'error' => {
+                  'code' => 404,
+                  'message' => "Object does not exist on the server or you don't have permissions to access it",
+                }
+              }])
+          end
+        end
+
+        context 'when downloading a lfs object that does not exist' do
+          let(:body) do
+            { 'operation' => 'download',
+              'objects' => [
+                { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+                  'size' => 1575078
+                }]
+            }
+          end
+
+          it 'responds with status 200' do
+            expect(response).to have_http_status(200)
+          end
+
+          it 'with an 404 for specific object' do
+            expect(json_response).to eq('objects' => [
+              { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+                'size' => 1575078,
+                'error' => {
+                  'code' => 404,
+                  'message' => "Object does not exist on the server or you don't have permissions to access it",
+                }
+              }])
+          end
+        end
+
+        context 'when downloading one new and one existing lfs object' do
+          let(:body) do
+            { 'operation' => 'download',
+              'objects' => [
+                { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+                  'size' => 1575078
+                },
+                { 'oid' => sample_oid,
+                  'size' => sample_size
+                }
+              ]
+            }
+          end
+
+          let(:update_lfs_permissions) do
+            project.lfs_objects << lfs_object
+          end
+
+          it 'responds with status 200' do
+            expect(response).to have_http_status(200)
+          end
+
+          it 'responds with upload hypermedia link for the new object' do
+            expect(json_response).to eq('objects' => [
+              { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+                'size' => 1575078,
+                'error' => {
+                  'code' => 404,
+                  'message' => "Object does not exist on the server or you don't have permissions to access it",
+                }
+              },
+              { 'oid' => sample_oid,
+                'size' => sample_size,
+                'actions' => {
+                  'download' => {
+                    'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
+                    'header' => { 'Authorization' => authorization }
+                  }
+                }
+              }])
+          end
+        end
+      end
+
+      context 'when user is authenticated' do
+        let(:authorization) { authorize_user }
+
+        let(:update_user_permissions) do
+          project.team << [user, role]
+        end
+
+        it_behaves_like 'an authorized requests' do
+          let(:role) { :reporter }
+        end
+
+        context 'when user does is not member of the project' do
+          let(:role) { :guest }
+
+          it 'responds with 403' do
+            expect(response).to have_http_status(403)
+          end
+        end
+
+        context 'when user does not have download access' do
+          let(:role) { :guest }
+
+          it 'responds with 403' do
+            expect(response).to have_http_status(403)
+          end
+        end
+      end
+
+      context 'when CI is authorized' do
+        let(:authorization) { authorize_ci_project }
+
+        it_behaves_like 'an authorized requests'
+      end
+
+      context 'when user is not authenticated' do
+        describe 'is accessing public project' do
+          let(:project) { create(:project, :public) }
+
+          let(:update_lfs_permissions) do
+            project.lfs_objects << lfs_object
+          end
+
+          it 'responds with status 200 and href to download' do
+            expect(response).to have_http_status(200)
+          end
+
+          it 'responds with status 200 and href to download' do
+            expect(json_response).to eq('objects' => [
+              { 'oid' => sample_oid,
+                'size' => sample_size,
+                'actions' => {
+                  'download' => {
+                    'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
+                    'header' => {}
+                  }
+                }
+              }])
+          end
+        end
+
+        describe 'is accessing non-public project' do
+          let(:update_lfs_permissions) do
+            project.lfs_objects << lfs_object
+          end
+
+          it 'responds with authorization required' do
+            expect(response).to have_http_status(401)
+          end
+        end
+      end
+    end
+
+    describe 'upload' do
+      let(:project) { create(:project, :public) }
+      let(:body) do
+        { 'operation' => 'upload',
+          'objects' => [
+            { 'oid' => sample_oid,
+              'size' => sample_size
+            }]
+        }
+      end
+
+      describe 'when request is authenticated' do
+        describe 'when user has project push access' do
+          let(:authorization) { authorize_user }
+
+          let(:update_user_permissions) do
+            project.team << [user, :developer]
+          end
+
+          context 'when pushing an lfs object that already exists' do
+            let(:other_project) { create(:empty_project) }
+            let(:update_lfs_permissions) do
+              other_project.lfs_objects << lfs_object
+            end
+
+            it 'responds with status 200' do
+              expect(response).to have_http_status(200)
+            end
+
+            it 'responds with links the object to the project' do
+              expect(json_response['objects']).to be_kind_of(Array)
+              expect(json_response['objects'].first['oid']).to eq(sample_oid)
+              expect(json_response['objects'].first['size']).to eq(sample_size)
+              expect(lfs_object.projects.pluck(:id)).not_to include(project.id)
+              expect(lfs_object.projects.pluck(:id)).to include(other_project.id)
+              expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}")
+              expect(json_response['objects'].first['actions']['upload']['header']).to eq('Authorization' => authorization)
+            end
+          end
+
+          context 'when pushing a lfs object that does not exist' do
+            let(:body) do
+              { 'operation' => 'upload',
+                'objects' => [
+                  { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+                    'size' => 1575078
+                  }]
+              }
+            end
+
+            it 'responds with status 200' do
+              expect(response).to have_http_status(200)
+            end
+
+            it 'responds with upload hypermedia link' do
+              expect(json_response['objects']).to be_kind_of(Array)
+              expect(json_response['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
+              expect(json_response['objects'].first['size']).to eq(1575078)
+              expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
+              expect(json_response['objects'].first['actions']['upload']['header']).to eq('Authorization' => authorization)
+            end
+          end
+
+          context 'when pushing one new and one existing lfs object' do
+            let(:body) do
+              { 'operation' => 'upload',
+                'objects' => [
+                  { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+                    'size' => 1575078
+                  },
+                  { 'oid' => sample_oid,
+                    'size' => sample_size
+                  }
+                ]
+              }
+            end
+
+            let(:update_lfs_permissions) do
+              project.lfs_objects << lfs_object
+            end
+
+            it 'responds with status 200' do
+              expect(response).to have_http_status(200)
+            end
+
+            it 'responds with upload hypermedia link for the new object' do
+              expect(json_response['objects']).to be_kind_of(Array)
+
+              expect(json_response['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
+              expect(json_response['objects'].first['size']).to eq(1575078)
+              expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{project.http_url_to_repo}/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
+              expect(json_response['objects'].first['actions']['upload']['header']).to eq("Authorization" => authorization)
+
+              expect(json_response['objects'].last['oid']).to eq(sample_oid)
+              expect(json_response['objects'].last['size']).to eq(sample_size)
+              expect(json_response['objects'].last).not_to have_key('actions')
+            end
+          end
+        end
+
+        context 'when user does not have push access' do
+          let(:authorization) { authorize_user }
+
+          it 'responds with 403' do
+            expect(response).to have_http_status(403)
+          end
+        end
+
+        context 'when CI is authorized' do
+          let(:authorization) { authorize_ci_project }
+
+          it 'responds with 401' do
+            expect(response).to have_http_status(401)
+          end
+        end
+      end
+
+      context 'when user is not authenticated' do
+        context 'when user has push access' do
+          let(:update_user_permissions) do
+            project.team << [user, :master]
+          end
+
+          it 'responds with status 401' do
+            expect(response).to have_http_status(401)
+          end
+        end
+
+        context 'when user does not have push access' do
+          it 'responds with status 401' do
+            expect(response).to have_http_status(401)
+          end
+        end
+      end
+
+      context 'when CI is authorized' do
+        let(:authorization) { authorize_ci_project }
+
+        it 'responds with status 403' do
+          expect(response).to have_http_status(401)
+        end
+      end
+    end
+
+    describe 'unsupported' do
+      let(:project) { create(:empty_project) }
+      let(:body) do
+        { 'operation' => 'other',
+          'objects' => [
+            { 'oid' => sample_oid,
+              'size' => sample_size
+            }]
+        }
+      end
+
+      it 'responds with status 404' do
+        expect(response).to have_http_status(404)
+      end
+    end
+  end
+
+  describe 'when pushing a lfs object' do
+    before do
+      enable_lfs
+    end
+
+    shared_examples 'unauthorized' do
+      context 'and request is sent by gitlab-workhorse to authorize the request' do
+        before do
+          put_authorize
+        end
+
+        it 'responds with status 401' do
+          expect(response).to have_http_status(401)
+        end
+      end
+
+      context 'and request is sent by gitlab-workhorse to finalize the upload' do
+        before do
+          put_finalize
+        end
+
+        it 'responds with status 401' do
+          expect(response).to have_http_status(401)
+        end
+      end
+
+      context 'and request is sent with a malformed headers' do
+        before do
+          put_finalize('cat /etc/passwd')
+        end
+
+        it 'does not recognize it as a valid lfs command' do
+          expect(response).to have_http_status(403)
+        end
+      end
+    end
+
+    shared_examples 'forbidden' do
+      context 'and request is sent by gitlab-workhorse to authorize the request' do
+        before do
+          put_authorize
+        end
+
+        it 'responds with 403' do
+          expect(response).to have_http_status(403)
+        end
+      end
+
+      context 'and request is sent by gitlab-workhorse to finalize the upload' do
+        before do
+          put_finalize
+        end
+
+        it 'responds with 403' do
+          expect(response).to have_http_status(403)
+        end
+      end
+    end
+
+    describe 'to one project' do
+      let(:project) { create(:empty_project) }
+
+      describe 'when user is authenticated' do
+        let(:authorization) { authorize_user }
+
+        describe 'when user has push access to the project' do
+          before do
+            project.team << [user, :developer]
+          end
+
+          context 'and request is sent by gitlab-workhorse to authorize the request' do
+            before do
+              put_authorize
+            end
+
+            it 'responds with status 200' do
+              expect(response).to have_http_status(200)
+            end
+
+            it 'responds with status 200, location of lfs store and object details' do
+              expect(json_response['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
+              expect(json_response['LfsOid']).to eq(sample_oid)
+              expect(json_response['LfsSize']).to eq(sample_size)
+            end
+          end
+
+          context 'and request is sent by gitlab-workhorse to finalize the upload' do
+            before do
+              put_finalize
+            end
+
+            it 'responds with status 200' do
+              expect(response).to have_http_status(200)
+            end
+
+            it 'lfs object is linked to the project' do
+              expect(lfs_object.projects.pluck(:id)).to include(project.id)
+            end
+          end
+        end
+
+        describe 'and user does not have push access' do
+          it_behaves_like 'forbidden'
+        end
+      end
+
+      context 'when CI is authenticated' do
+        let(:authorization) { authorize_ci_project }
+
+        it_behaves_like 'unauthorized'
+      end
+
+      context 'for unauthenticated' do
+        it_behaves_like 'unauthorized'
+      end
+    end
+
+    describe 'to a forked project' do
+      let(:upstream_project) { create(:project, :public) }
+      let(:project_owner) { create(:user) }
+      let(:project) { fork_project(upstream_project, project_owner) }
+
+      describe 'when user is authenticated' do
+        let(:authorization) { authorize_user }
+
+        describe 'when user has push access to the project' do
+          before do
+            project.team << [user, :developer]
+          end
+
+          context 'and request is sent by gitlab-workhorse to authorize the request' do
+            before do
+              put_authorize
+            end
+
+            it 'responds with status 200' do
+              expect(response).to have_http_status(200)
+            end
+
+            it 'with location of lfs store and object details' do
+              expect(json_response['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
+              expect(json_response['LfsOid']).to eq(sample_oid)
+              expect(json_response['LfsSize']).to eq(sample_size)
+            end
+          end
+
+          context 'and request is sent by gitlab-workhorse to finalize the upload' do
+            before do
+              put_finalize
+            end
+
+            it 'responds with status 200' do
+              expect(response).to have_http_status(200)
+            end
+
+            it 'lfs object is linked to the source project' do
+              expect(lfs_object.projects.pluck(:id)).to include(upstream_project.id)
+            end
+          end
+        end
+
+        describe 'and user does not have push access' do
+          it_behaves_like 'forbidden'
+        end
+      end
+
+      context 'when CI is authenticated' do
+        let(:authorization) { authorize_ci_project }
+
+        it_behaves_like 'unauthorized'
+      end
+
+      context 'for unauthenticated' do
+        it_behaves_like 'unauthorized'
+      end
+
+      describe 'and second project not related to fork or a source project' do
+        let(:second_project) { create(:empty_project) }
+        let(:authorization) { authorize_user }
+
+        before do
+          second_project.team << [user, :master]
+          upstream_project.lfs_objects << lfs_object
+        end
+
+        context 'when pushing the same lfs object to the second project' do
+          before do
+            put "#{second_project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}", nil,
+                headers.merge('X-Gitlab-Lfs-Tmp' => lfs_tmp_file).compact
+          end
+
+          it 'responds with status 200' do
+            expect(response).to have_http_status(200)
+          end
+
+          it 'links the lfs object to the project' do
+            expect(lfs_object.projects.pluck(:id)).to include(second_project.id, upstream_project.id)
+          end
+        end
+      end
+    end
+
+    def put_authorize
+      put "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}/authorize", nil, headers
+    end
+
+    def put_finalize(lfs_tmp = lfs_tmp_file)
+      put "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}", nil,
+          headers.merge('X-Gitlab-Lfs-Tmp' => lfs_tmp).compact
+    end
+
+    def lfs_tmp_file
+      "#{sample_oid}012345678"
+    end
+  end
+
+  def enable_lfs
+    allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
+  end
+
+  def authorize_ci_project
+    ActionController::HttpAuthentication::Basic.encode_credentials('gitlab-ci-token', project.runners_token)
+  end
+
+  def authorize_user
+    ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
+  end
+
+  def fork_project(project, user, object = nil)
+    allow(RepositoryForkWorker).to receive(:perform_async).and_return(true)
+    Projects::ForkService.new(project, user, {}).execute
+  end
+
+  def post_json(url, body = nil, headers = nil)
+    post(url, body.try(:to_json), (headers || {}).merge('Content-Type' => 'application/json'))
+  end
+
+  def json_response
+    @json_response ||= JSON.parse(response.body)
+  end
+end
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index 8a8e131c57b823c40a07f9983ab714503912de60..2c755919456cf9207e6ddfa94c8673fee31237b1 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -98,7 +98,7 @@ describe SnippetsController, "routing" do
 end
 
 #            help GET /help(.:format)                 help#index
-#       help_page GET /help/:category/:file(.:format) help#show {:category=>/.*/, :file=>/[^\/\.]+/}
+#       help_page GET /help/*path(.:format)           help#show
 #  help_shortcuts GET /help/shortcuts(.:format)       help#shortcuts
 #         help_ui GET /help/ui(.:format)              help#ui
 describe HelpController, "routing" do
@@ -109,23 +109,19 @@ describe HelpController, "routing" do
   it 'to #show' do
     path = '/help/markdown/markdown.md'
     expect(get(path)).to route_to('help#show',
-                                  category: 'markdown',
-                                  file: 'markdown',
+                                  path: 'markdown/markdown',
                                   format: 'md')
 
     path = '/help/workflow/protected_branches/protected_branches1.png'
     expect(get(path)).to route_to('help#show',
-                                  category: 'workflow/protected_branches',
-                                  file: 'protected_branches1',
+                                  path: 'workflow/protected_branches/protected_branches1',
                                   format: 'png')
-  end
-
-  it 'to #shortcuts' do
-    expect(get('/help/shortcuts')).to route_to('help#shortcuts')
-  end
-
-  it 'to #ui' do
-    expect(get('/help/ui')).to route_to('help#ui')
+    path = '/help/shortcuts'
+    expect(get(path)).to route_to('help#show',
+                                  path: 'shortcuts')
+    path = '/help/ui'
+    expect(get(path)).to route_to('help#show',
+                                  path: 'ui')
   end
 end
 
diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb
index 67777ad48bc69c45cd164c99f8297ea9c998d502..7cc71f706ce6c9638b473408ece6f204d5335125 100644
--- a/spec/services/auth/container_registry_authentication_service_spec.rb
+++ b/spec/services/auth/container_registry_authentication_service_spec.rb
@@ -87,51 +87,105 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
   end
 
   context 'user authorization' do
-    let(:project) { create(:project) }
     let(:current_user) { create(:user) }
 
-    context 'allow to use scope-less authentication' do
-      it_behaves_like 'a valid token'
-    end
+    context 'for private project' do
+      let(:project) { create(:empty_project) }
 
-    context 'allow developer to push images' do
-      before { project.team << [current_user, :developer] }
+      context 'allow to use scope-less authentication' do
+        it_behaves_like 'a valid token'
+      end
 
-      let(:current_params) do
-        { scope: "repository:#{project.path_with_namespace}:push" }
+      context 'allow developer to push images' do
+        before { project.team << [current_user, :developer] }
+
+        let(:current_params) do
+          { scope: "repository:#{project.path_with_namespace}:push" }
+        end
+
+        it_behaves_like 'a pushable'
       end
 
-      it_behaves_like 'a pushable'
-    end
+      context 'allow reporter to pull images' do
+        before { project.team << [current_user, :reporter] }
+
+        let(:current_params) do
+          { scope: "repository:#{project.path_with_namespace}:pull" }
+        end
 
-    context 'allow reporter to pull images' do
-      before { project.team << [current_user, :reporter] }
+        it_behaves_like 'a pullable'
+      end
 
-      let(:current_params) do
-        { scope: "repository:#{project.path_with_namespace}:pull" }
+      context 'return a least of privileges' do
+        before { project.team << [current_user, :reporter] }
+
+        let(:current_params) do
+          { scope: "repository:#{project.path_with_namespace}:push,pull" }
+        end
+
+        it_behaves_like 'a pullable'
       end
 
-      it_behaves_like 'a pullable'
+      context 'disallow guest to pull or push images' do
+        before { project.team << [current_user, :guest] }
+
+        let(:current_params) do
+          { scope: "repository:#{project.path_with_namespace}:pull,push" }
+        end
+
+        it_behaves_like 'an inaccessible'
+      end
     end
 
-    context 'return a least of privileges' do
-      before { project.team << [current_user, :reporter] }
+    context 'for public project' do
+      let(:project) { create(:empty_project, :public) }
 
-      let(:current_params) do
-        { scope: "repository:#{project.path_with_namespace}:push,pull" }
+      context 'allow anyone to pull images' do
+        let(:current_params) do
+          { scope: "repository:#{project.path_with_namespace}:pull" }
+        end
+
+        it_behaves_like 'a pullable'
       end
 
-      it_behaves_like 'a pullable'
+      context 'disallow anyone to push images' do
+        let(:current_params) do
+          { scope: "repository:#{project.path_with_namespace}:push" }
+        end
+
+        it_behaves_like 'an inaccessible'
+      end
     end
 
-    context 'disallow guest to pull or push images' do
-      before { project.team << [current_user, :guest] }
+    context 'for internal project' do
+      let(:project) { create(:empty_project, :internal) }
 
-      let(:current_params) do
-        { scope: "repository:#{project.path_with_namespace}:pull,push" }
+      context 'for internal user' do
+        context 'allow anyone to pull images' do
+          let(:current_params) do
+            { scope: "repository:#{project.path_with_namespace}:pull" }
+          end
+
+          it_behaves_like 'a pullable'
+        end
+
+        context 'disallow anyone to push images' do
+          let(:current_params) do
+            { scope: "repository:#{project.path_with_namespace}:push" }
+          end
+
+          it_behaves_like 'an inaccessible'
+        end
       end
 
-      it_behaves_like 'an inaccessible'
+      context 'for external user' do
+        let(:current_user) { create(:user, external: true) }
+        let(:current_params) do
+          { scope: "repository:#{project.path_with_namespace}:pull,push" }
+        end
+
+        it_behaves_like 'an inaccessible'
+      end
     end
   end
 
diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb
index ae4b7aca8206bd78b36bb313f2677fb299fb6f43..b72e0bd3dbeb4708fda3c6481c1923562aadbd0b 100644
--- a/spec/services/ci/create_trigger_request_service_spec.rb
+++ b/spec/services/ci/create_trigger_request_service_spec.rb
@@ -9,7 +9,7 @@ describe Ci::CreateTriggerRequestService, services: true do
     stub_ci_pipeline_to_return_yaml_file
   end
 
-  describe :execute do
+  describe '#execute' do
     context 'valid params' do
       subject { service.execute(project, trigger, 'master') }
 
diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb
index 476a888e394fb8878f154bc8620b6b6d375767d9..3a3e3efe709ada4756d963b511f126560017fc6a 100644
--- a/spec/services/ci/image_for_build_service_spec.rb
+++ b/spec/services/ci/image_for_build_service_spec.rb
@@ -8,7 +8,7 @@ module Ci
     let(:commit) { project.ensure_pipeline(commit_sha, 'master') }
     let(:build) { FactoryGirl.create(:ci_build, pipeline: commit) }
 
-    describe :execute do
+    describe '#execute' do
       before { build }
 
       context 'branch name' do
diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb
index f28f2f1438ddf2e8cba207e0464cbe8d2ce4044b..026d0ca65340146400d3c9f1a43f244eb44a1bf4 100644
--- a/spec/services/ci/register_build_service_spec.rb
+++ b/spec/services/ci/register_build_service_spec.rb
@@ -13,7 +13,7 @@ module Ci
       specific_runner.assign_to(project)
     end
 
-    describe :execute do
+    describe '#execute' do
       context 'runner follow tag list' do
         it "picks build with the same tag" do
           pending_build.tag_list = ["linux"]
diff --git a/spec/services/create_commit_builds_service_spec.rb b/spec/services/create_commit_builds_service_spec.rb
index 309213bd44ccc17fa014ed26273b1b1d06d0f1e6..d4c5e584421627b21d1a08fc59d975bf0aa6d4b4 100644
--- a/spec/services/create_commit_builds_service_spec.rb
+++ b/spec/services/create_commit_builds_service_spec.rb
@@ -3,13 +3,13 @@ require 'spec_helper'
 describe CreateCommitBuildsService, services: true do
   let(:service) { CreateCommitBuildsService.new }
   let(:project) { FactoryGirl.create(:empty_project) }
-  let(:user) { nil }
+  let(:user) { create(:user) }
 
   before do
     stub_ci_pipeline_to_return_yaml_file
   end
 
-  describe :execute do
+  describe '#execute' do
     context 'valid params' do
       let(:pipeline) do
         service.execute(project, user,
@@ -24,6 +24,7 @@ describe CreateCommitBuildsService, services: true do
       it { expect(pipeline).to be_valid }
       it { expect(pipeline).to be_persisted }
       it { expect(pipeline).to eq(project.pipelines.last) }
+      it { expect(pipeline).to have_attributes(user: user) }
       it { expect(pipeline.builds.first).to be_kind_of(Ci::Build) }
     end
 
diff --git a/spec/services/create_deployment_service_spec.rb b/spec/services/create_deployment_service_spec.rb
index 654e441f3cd44f404d0d40facadf0fdcd5e3f69e..8da2a2b3c1b2c430d51cd93bce5c09383abf8b79 100644
--- a/spec/services/create_deployment_service_spec.rb
+++ b/spec/services/create_deployment_service_spec.rb
@@ -89,6 +89,12 @@ describe CreateDeploymentService, services: true do
         expect_any_instance_of(described_class).to receive(:execute)
         subject
       end
+
+      it 'is set as deployable' do
+        subject
+
+        expect(Deployment.last.deployable).to eq(deployable)
+      end
     end
 
     context 'without environment specified' do
@@ -105,6 +111,8 @@ describe CreateDeploymentService, services: true do
 
       context 'when build succeeds' do
         it_behaves_like 'does create environment and deployment' do
+          let(:deployable) { build }
+
           subject { build.success }
         end
       end
@@ -114,6 +122,14 @@ describe CreateDeploymentService, services: true do
           subject { build.drop }
         end
       end
+
+      context 'when build is retried' do
+        it_behaves_like 'does create environment and deployment' do
+          let(:deployable) { Ci::Build.retry(build) }
+
+          subject { deployable.success }
+        end
+      end
     end
   end
 end
diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb
index f6dc9d4008f3e19ace0d6cf1dfa0d677bb390f6f..789836f71bb2d339182190bbb3329135046a7ac5 100644
--- a/spec/services/event_create_service_spec.rb
+++ b/spec/services/event_create_service_spec.rb
@@ -4,7 +4,7 @@ describe EventCreateService, services: true do
   let(:service) { EventCreateService.new }
 
   describe 'Issues' do
-    describe :open_issue do
+    describe '#open_issue' do
       let(:issue) { create(:issue) }
 
       it { expect(service.open_issue(issue, issue.author)).to be_truthy }
@@ -14,7 +14,7 @@ describe EventCreateService, services: true do
       end
     end
 
-    describe :close_issue do
+    describe '#close_issue' do
       let(:issue) { create(:issue) }
 
       it { expect(service.close_issue(issue, issue.author)).to be_truthy }
@@ -24,7 +24,7 @@ describe EventCreateService, services: true do
       end
     end
 
-    describe :reopen_issue do
+    describe '#reopen_issue' do
       let(:issue) { create(:issue) }
 
       it { expect(service.reopen_issue(issue, issue.author)).to be_truthy }
@@ -36,7 +36,7 @@ describe EventCreateService, services: true do
   end
 
   describe 'Merge Requests' do
-    describe :open_mr do
+    describe '#open_mr' do
       let(:merge_request) { create(:merge_request) }
 
       it { expect(service.open_mr(merge_request, merge_request.author)).to be_truthy }
@@ -46,7 +46,7 @@ describe EventCreateService, services: true do
       end
     end
 
-    describe :close_mr do
+    describe '#close_mr' do
       let(:merge_request) { create(:merge_request) }
 
       it { expect(service.close_mr(merge_request, merge_request.author)).to be_truthy }
@@ -56,7 +56,7 @@ describe EventCreateService, services: true do
       end
     end
 
-    describe :merge_mr do
+    describe '#merge_mr' do
       let(:merge_request) { create(:merge_request) }
 
       it { expect(service.merge_mr(merge_request, merge_request.author)).to be_truthy }
@@ -66,7 +66,7 @@ describe EventCreateService, services: true do
       end
     end
 
-    describe :reopen_mr do
+    describe '#reopen_mr' do
       let(:merge_request) { create(:merge_request) }
 
       it { expect(service.reopen_mr(merge_request, merge_request.author)).to be_truthy }
@@ -80,7 +80,7 @@ describe EventCreateService, services: true do
   describe 'Milestone' do
     let(:user) { create :user }
 
-    describe :open_milestone do
+    describe '#open_milestone' do
       let(:milestone) { create(:milestone) }
 
       it { expect(service.open_milestone(milestone, user)).to be_truthy }
@@ -90,7 +90,7 @@ describe EventCreateService, services: true do
       end
     end
 
-    describe :close_mr do
+    describe '#close_mr' do
       let(:milestone) { create(:milestone) }
 
       it { expect(service.close_milestone(milestone, user)).to be_truthy }
@@ -100,7 +100,7 @@ describe EventCreateService, services: true do
       end
     end
 
-    describe :destroy_mr do
+    describe '#destroy_mr' do
       let(:milestone) { create(:milestone) }
 
       it { expect(service.destroy_milestone(milestone, user)).to be_truthy }
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index 62b25709a5d21370b3191bbf7067bf7a52762491..67a919ba8ee32016a6e31e0d421cf23e1dd8b792 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -12,7 +12,7 @@ describe Issues::CloseService, services: true do
     project.team << [user2, :developer]
   end
 
-  describe :execute do
+  describe '#execute' do
     context "valid params" do
       before do
         perform_enqueued_jobs do
diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb
index 8443a00e70cbcd8f3daf0408e6cab0bf1ff377c8..c1db4f3284b18eec69923ac1b535811b18a58a48 100644
--- a/spec/services/merge_requests/close_service_spec.rb
+++ b/spec/services/merge_requests/close_service_spec.rb
@@ -12,7 +12,7 @@ describe MergeRequests::CloseService, services: true do
     project.team << [user2, :developer]
   end
 
-  describe :execute do
+  describe '#execute' do
     context 'valid params' do
       let(:service) { MergeRequests::CloseService.new(project, user, {}) }
 
diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb
index e433f49872de010a96c080ee28268ee3bf14ec8d..d0b55d2d5094bdbf6b908ab8f77f211cfd620841 100644
--- a/spec/services/merge_requests/create_service_spec.rb
+++ b/spec/services/merge_requests/create_service_spec.rb
@@ -5,7 +5,7 @@ describe MergeRequests::CreateService, services: true do
   let(:user) { create(:user) }
   let(:assignee) { create(:user) }
 
-  describe :execute do
+  describe '#execute' do
     context 'valid params' do
       let(:opts) do
         {
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index 2f72cd600715689686dc4ad10b81d5c19d4afcaa..f5bf3c1e3673d4b7c31f8151afaf7c1304e4518f 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -11,7 +11,7 @@ describe MergeRequests::MergeService, services: true do
     project.team << [user2, :developer]
   end
 
-  describe :execute do
+  describe '#execute' do
     context 'valid params' do
       let(:service) { MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') }
 
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 7d5cb876063ebe31c5ef0a0fc67c6ef2afe8b753..06f56d85aa86955c733bc0ff0e90a51eff3b5469 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -5,7 +5,7 @@ describe MergeRequests::RefreshService, services: true do
   let(:user) { create(:user) }
   let(:service) { MergeRequests::RefreshService }
 
-  describe :execute do
+  describe '#execute' do
     before do
       @user = create(:user)
       group = create(:group)
diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb
index ac0221998f55d2d06317b9084548e871fbbbe939..88c9c640514533f76918e209eafeacc165e8db47 100644
--- a/spec/services/merge_requests/reopen_service_spec.rb
+++ b/spec/services/merge_requests/reopen_service_spec.rb
@@ -11,7 +11,7 @@ describe MergeRequests::ReopenService, services: true do
     project.team << [user2, :developer]
   end
 
-  describe :execute do
+  describe '#execute' do
     context 'valid params' do
       let(:service) { MergeRequests::ReopenService.new(project, user, {}) }
 
diff --git a/spec/services/milestones/close_service_spec.rb b/spec/services/milestones/close_service_spec.rb
index 1cd6eb2ab382400f77e3b313e4826d7bf3260f5f..5d400299be0ba9a13e545351a8df958fcdbf2ad3 100644
--- a/spec/services/milestones/close_service_spec.rb
+++ b/spec/services/milestones/close_service_spec.rb
@@ -9,7 +9,7 @@ describe Milestones::CloseService, services: true do
     project.team << [user, :master]
   end
 
-  describe :execute do
+  describe '#execute' do
     before do
       Milestones::CloseService.new(project, user, {}).execute(milestone)
     end
diff --git a/spec/services/milestones/create_service_spec.rb b/spec/services/milestones/create_service_spec.rb
index c793026e300bfce2b9c6d84d41eebd6c06ac4bd1..6d29edb449a8477ddf93c2aa9cc721e747e1f321 100644
--- a/spec/services/milestones/create_service_spec.rb
+++ b/spec/services/milestones/create_service_spec.rb
@@ -4,7 +4,7 @@ describe Milestones::CreateService, services: true do
   let(:project) { create(:empty_project) }
   let(:user) { create(:user) }
 
-  describe :execute do
+  describe '#execute' do
     context "valid params" do
       before do
         project.team << [user, :master]
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index 35f576874b82711f13fc7c5856067e143131c6db..32753e84b314c7e7391e54ea9fc9846c79ca6c46 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -5,7 +5,7 @@ describe Notes::CreateService, services: true do
   let(:issue) { create(:issue, project: project) }
   let(:user) { create(:user) }
 
-  describe :execute do
+  describe '#execute' do
     context "valid params" do
       before do
         project.team << [user, :master]
diff --git a/spec/services/notes/post_process_service_spec.rb b/spec/services/notes/post_process_service_spec.rb
index d4c50f824c18017efcdb2ff1075db1a1f24319bb..e33a611929b7229d3d6d4ce7b4a73c7c7e85816a 100644
--- a/spec/services/notes/post_process_service_spec.rb
+++ b/spec/services/notes/post_process_service_spec.rb
@@ -5,7 +5,7 @@ describe Notes::PostProcessService, services: true do
   let(:issue) { create(:issue, project: project) }
   let(:user) { create(:user) }
 
-  describe :execute do
+  describe '#execute' do
     before do
       project.team << [user, :master]
       note_opts = {
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 54719cbb8d85b6ae66f880f1d7f34540fe7186d7..9fc93f325f72919fd3849a400106de5a04bfe15e 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -50,7 +50,7 @@ describe NotificationService, services: true do
         update_custom_notification(:new_note, @u_custom_global)
       end
 
-      describe :new_note do
+      describe '#new_note' do
         it do
           add_users_with_subscription(note.project, issue)
 
@@ -293,6 +293,30 @@ describe NotificationService, services: true do
         end
       end
     end
+
+    context "merge request diff note" do
+      let(:project) { create(:project) }
+      let(:user) { create(:user) }
+      let(:merge_request) { create(:merge_request, source_project: project, assignee: user) }
+      let(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request) }
+
+      before do
+        build_team(note.project)
+        project.team << [merge_request.author, :master]
+        project.team << [merge_request.assignee, :master]
+      end
+
+      describe '#new_note' do
+        it "records sent notifications" do
+          # Ensure create SentNotification by noteable = merge_request 6 times, not noteable = note
+          expect(SentNotification).to receive(:record_note).with(note, any_args).exactly(4).times.and_call_original
+
+          notification.new_note(note)
+
+          expect(SentNotification.last.position).to eq(note.position)
+        end
+      end
+    end
   end
 
   describe 'Issues' do
diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb
index bd4dc6a0f7989d3c2815991b8a04230d53e0c1a2..ad0d58672b3df13fd90e703932cfbe30eeb469d9 100644
--- a/spec/services/projects/housekeeping_service_spec.rb
+++ b/spec/services/projects/housekeeping_service_spec.rb
@@ -12,18 +12,28 @@ describe Projects::HousekeepingService do
 
     it 'enqueues a sidekiq job' do
       expect(subject).to receive(:try_obtain_lease).and_return(true)
-      expect(GitlabShellOneShotWorker).to receive(:perform_async).with(:gc, project.repository_storage_path, project.path_with_namespace)
+      expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id)
 
       subject.execute
-      expect(project.pushes_since_gc).to eq(0)
+      expect(project.reload.pushes_since_gc).to eq(0)
     end
 
-    it 'does not enqueue a job when no lease can be obtained' do
-      expect(subject).to receive(:try_obtain_lease).and_return(false)
-      expect(GitlabShellOneShotWorker).not_to receive(:perform_async)
+    context 'when no lease can be obtained' do
+      before(:each) do
+        expect(subject).to receive(:try_obtain_lease).and_return(false)
+      end
 
-      expect { subject.execute }.to raise_error(Projects::HousekeepingService::LeaseTaken)
-      expect(project.pushes_since_gc).to eq(0)
+      it 'does not enqueue a job' do
+        expect(GitGarbageCollectWorker).not_to receive(:perform_async)
+
+        expect { subject.execute }.to raise_error(Projects::HousekeepingService::LeaseTaken)
+      end
+
+      it 'does not reset pushes_since_gc' do
+        expect do
+          expect { subject.execute }.to raise_error(Projects::HousekeepingService::LeaseTaken)
+        end.not_to change { project.pushes_since_gc }.from(3)
+      end
     end
   end
 
@@ -39,10 +49,24 @@ describe Projects::HousekeepingService do
   end
 
   describe 'increment!' do
+    let(:lease_key) { "project_housekeeping:increment!:#{project.id}" }
+
     it 'increments the pushes_since_gc counter' do
-      expect(project.pushes_since_gc).to eq(0)
-      subject.increment!
-      expect(project.pushes_since_gc).to eq(1)
+      lease = double(:lease, try_obtain: true)
+      expect(Gitlab::ExclusiveLease).to receive(:new).with(lease_key, anything).and_return(lease)
+
+      expect do
+        subject.increment!
+      end.to change { project.pushes_since_gc }.from(0).to(1)
+    end
+
+    it 'does not increment when no lease can be obtained' do
+      lease = double(:lease, try_obtain: false)
+      expect(Gitlab::ExclusiveLease).to receive(:new).with(lease_key, anything).and_return(lease)
+
+      expect do
+        subject.increment!
+      end.not_to change { project.pushes_since_gc }
     end
   end
 end
diff --git a/spec/services/test_hook_service_spec.rb b/spec/services/test_hook_service_spec.rb
index f034f251ba4dfcd5148457a3fc79aec2ab8d029a..4f47e89b4b57d7bb2dc892291d87eb47cd37584b 100644
--- a/spec/services/test_hook_service_spec.rb
+++ b/spec/services/test_hook_service_spec.rb
@@ -5,7 +5,7 @@ describe TestHookService, services: true do
   let(:project) { create :project }
   let(:hook)    { create :project_hook, project: project }
 
-  describe :execute do
+  describe '#execute' do
     it "should execute successfully" do
       stub_request(:post, hook.url).to_return(status: 200)
       expect(TestHookService.new.execute(hook, user)).to be_truthy
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index b45225367244ba926d7445c2635c9e37953242f7..34d8ea9090e34be57b61b02b3b5b1269ad93f131 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -35,8 +35,11 @@ describe TodoService, services: true do
         should_not_create_any_todo { service.new_issue(unassigned_issue, author) }
       end
 
-      it 'does not create a todo if assignee is the current user' do
-        should_not_create_any_todo { service.new_issue(unassigned_issue, john_doe) }
+      it 'creates a todo if assignee is the current user' do
+        unassigned_issue.update_attribute(:assignee, john_doe)
+        service.new_issue(unassigned_issue, john_doe)
+
+        should_create_todo(user: john_doe, target: unassigned_issue, author: john_doe, action: Todo::ASSIGNED)
       end
 
       it 'creates a todo for each valid mentioned user' do
@@ -44,7 +47,7 @@ describe TodoService, services: true do
 
         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_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
@@ -57,7 +60,7 @@ describe TodoService, services: true do
         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)
+        should_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
       end
 
       context 'when a private group is mentioned' do
@@ -87,7 +90,7 @@ describe TodoService, services: true do
         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_create_todo(user: author, target: issue, action: Todo::MENTIONED)
         should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
       end
 
@@ -105,7 +108,7 @@ describe TodoService, services: true do
         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)
+        should_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
       end
 
       context 'issues with a task list' do
@@ -156,10 +159,11 @@ describe TodoService, services: true do
         should_not_create_any_todo { service.reassigned_issue(issue, author) }
       end
 
-      it 'does not create a todo if new assignee is the current user' do
+      it 'creates a todo if new assignee is the current user' do
         unassigned_issue.update_attribute(:assignee, john_doe)
+        service.reassigned_issue(unassigned_issue, john_doe)
 
-        should_not_create_any_todo { service.reassigned_issue(unassigned_issue, john_doe) }
+        should_create_todo(user: john_doe, target: unassigned_issue, author: john_doe, action: Todo::ASSIGNED)
       end
     end
 
@@ -250,7 +254,7 @@ describe TodoService, services: true do
         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_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
 
@@ -262,7 +266,7 @@ describe TodoService, services: true do
         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)
+        should_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
       end
 
       it 'creates a todo for each valid mentioned user when leaving a note on commit' do
@@ -270,7 +274,7 @@ describe TodoService, services: true do
 
         should_create_todo(user: member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
         should_create_todo(user: author, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
-        should_not_create_todo(user: john_doe, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
+        should_create_todo(user: john_doe, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
         should_not_create_todo(user: non_member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
       end
 
@@ -312,7 +316,7 @@ describe TodoService, services: true do
 
         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_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)
       end
@@ -325,7 +329,7 @@ describe TodoService, services: true do
         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_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
         should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
       end
 
@@ -382,10 +386,11 @@ describe TodoService, services: true do
         should_not_create_any_todo { service.reassigned_merge_request(mr_assigned, author) }
       end
 
-      it 'does not create a todo if new assignee is the current user' do
+      it 'creates a todo if new assignee is the current user' do
         mr_assigned.update_attribute(:assignee, john_doe)
+        service.reassigned_merge_request(mr_assigned, john_doe)
 
-        should_not_create_any_todo { service.reassigned_merge_request(mr_assigned, john_doe) }
+        should_create_todo(user: john_doe, target: mr_assigned, author: john_doe, action: Todo::ASSIGNED)
       end
     end
 
@@ -435,6 +440,24 @@ describe TodoService, services: true do
         should_create_todo(user: author, target: mr_unassigned, action: Todo::MARKED)
       end
     end
+
+    describe '#new_note' do
+      let(:mention) { john_doe.to_reference }
+      let(:diff_note_on_merge_request) { create(:diff_note_on_merge_request, project: project, noteable: mr_unassigned, author: author, note: "Hey #{mention}") }
+      let(:legacy_diff_note_on_merge_request) { create(:legacy_diff_note_on_merge_request, project: project, noteable: mr_unassigned, author: author, note: "Hey #{mention}") }
+
+      it 'creates a todo for mentioned user on new diff note' do
+        service.new_note(diff_note_on_merge_request, author)
+
+        should_create_todo(user: john_doe, target: mr_unassigned, author: author, action: Todo::MENTIONED, note: diff_note_on_merge_request)
+      end
+
+      it 'creates a todo for mentioned user on legacy diff note' do
+        service.new_note(legacy_diff_note_on_merge_request, author)
+
+        should_create_todo(user: john_doe, target: mr_unassigned, author: author, action: Todo::MENTIONED, note: legacy_diff_note_on_merge_request)
+      end
+    end
   end
 
   it 'updates cached counts when a todo is created' do
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 62097de27682ed2aff83d77e24db3ad7dec2395a..70d516e05e6ac73db950fc085635bc8ccc324588 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -33,7 +33,6 @@ RSpec.configure do |config|
   config.include LoginHelpers,          type: :feature
   config.include StubConfiguration
   config.include EmailHelpers
-  config.include RelativeUrl,         type: feature
   config.include TestEnv
   config.include ActiveJob::TestHelper
   config.include StubGitlabCalls
diff --git a/spec/support/capybara_helpers.rb b/spec/support/capybara_helpers.rb
index 9b5c3065eed423753949cc6dad4864ee54427ed9..b57a3493aff1b01411dc5f5a8a7549030966cbaf 100644
--- a/spec/support/capybara_helpers.rb
+++ b/spec/support/capybara_helpers.rb
@@ -27,6 +27,14 @@ module CapybaraHelpers
       end
     end
   end
+
+  # Refresh the page. Calling `visit current_url` doesn't seem to work consistently.
+  #
+  def refresh
+    url = current_url
+    visit 'about:blank'
+    visit url
+  end
 end
 
 RSpec.configure do |config|
diff --git a/spec/support/fake_u2f_device.rb b/spec/support/fake_u2f_device.rb
index 553fe9f1fbc8e163754330de4e816a0cc6dd5663..f550e9a0160f27c7568084146c168ec7276e097c 100644
--- a/spec/support/fake_u2f_device.rb
+++ b/spec/support/fake_u2f_device.rb
@@ -18,8 +18,8 @@ class FakeU2fDevice
 
   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])
+    challenge = @page.evaluate_script('gon.u2f.challenge')
+    json_response = u2f_device(app_id).sign_response(challenge)
 
     @page.execute_script("
     u2f.sign = function(appId, challenges, signRequests, callback) {
diff --git a/spec/support/login_helpers.rb b/spec/support/login_helpers.rb
index ffdf2bb0a8ac56c3ae534c43203e0cbd2f871085..e5f76afbfc0883755c305d24a254355f32408e85 100644
--- a/spec/support/login_helpers.rb
+++ b/spec/support/login_helpers.rb
@@ -37,6 +37,40 @@ module LoginHelpers
     Thread.current[:current_user] = user
   end
 
+  def login_via(provider, user, uid)
+    mock_auth_hash(provider, uid, user.email)
+    visit new_user_session_path
+    click_link provider
+  end
+
+  def mock_auth_hash(provider, uid, email)
+    # The mock_auth configuration allows you to set per-provider (or default)
+    # authentication hashes to return during integration testing.
+    OmniAuth.config.mock_auth[provider.to_sym] = OmniAuth::AuthHash.new({
+      provider: provider,
+      uid: uid,
+      info: {
+        name: 'mockuser',
+        email: email,
+        image: 'mock_user_thumbnail_url'
+      },
+      credentials: {
+        token: 'mock_token',
+        secret: 'mock_secret'
+      },
+      extra: {
+        raw_info: {
+          info: {
+            name: 'mockuser',
+            email: email,
+            image: 'mock_user_thumbnail_url'
+          }
+        }
+      }
+    })
+    Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[:saml]
+  end
+
   # Requires Javascript driver.
   def logout
     find(".header-user-dropdown-toggle").click
diff --git a/spec/support/omni_auth.rb b/spec/support/omni_auth.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0b1af4052fffbdc9581b677c49ed255c37b8b0d7
--- /dev/null
+++ b/spec/support/omni_auth.rb
@@ -0,0 +1 @@
+OmniAuth.config.test_mode = true
diff --git a/spec/support/relative_url.rb b/spec/support/relative_url.rb
deleted file mode 100644
index 72e3ccce75bc44d37f9b59bc08c544ec5c1eecbe..0000000000000000000000000000000000000000
--- a/spec/support/relative_url.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# Fix route helpers in tests (e.g. root_path, ...)
-module RelativeUrl
-  extend ActiveSupport::Concern
-
-  included do
-    default_url_options[:script_name] = Rails.application.config.relative_url_root
-  end
-end
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index 6b99b0f24cb0cb669c696a10875956007bd53791..bb6c84262f6144c91df9c57bb8cca0ab0c5649a9 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -5,19 +5,20 @@ module TestEnv
 
   # When developing the seed repository, comment out the branch you will modify.
   BRANCH_SHA = {
-    'empty-branch'     => '7efb185',
-    'flatten-dir'      => 'e56497b',
-    'feature'          => '0b4bc9a',
-    'feature_conflict' => 'bb5206f',
-    'fix'              => '48f0be4',
-    'improve/awesome'  => '5937ac0',
-    'markdown'         => '0ed8c6c',
-    'lfs'              => 'be93687',
-    'master'           => '5937ac0',
-    "'test'"           => 'e56497b',
-    'orphaned-branch'  => '45127a9',
-    'binary-encoding'  => '7b1cf43',
-    'gitattributes'    => '5a62481',
+    'empty-branch'          => '7efb185',
+    'flatten-dir'           => 'e56497b',
+    'feature'               => '0b4bc9a',
+    'feature_conflict'      => 'bb5206f',
+    'fix'                   => '48f0be4',
+    'improve/awesome'       => '5937ac0',
+    'markdown'              => '0ed8c6c',
+    'lfs'                   => 'be93687',
+    'master'                => '5937ac0',
+    "'test'"                => 'e56497b',
+    'orphaned-branch'       => '45127a9',
+    'binary-encoding'       => '7b1cf43',
+    'gitattributes'         => '5a62481',
+    'expand-collapse-diffs' => '4842455'
   }
 
   # gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c9f5aae0815b364f883060d8fcee62c67bd9a360
--- /dev/null
+++ b/spec/workers/git_garbage_collect_worker_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe GitGarbageCollectWorker do
+  let(:project) { create(:project) }
+  let(:shell) { Gitlab::Shell.new }
+
+  subject { GitGarbageCollectWorker.new }
+
+  before do
+    allow(subject).to receive(:gitlab_shell).and_return(shell)
+  end
+
+  describe "#perform" do
+    it "runs `git gc`" do
+      expect(shell).to receive(:gc).with(
+        project.repository_storage_path,
+        project.path_with_namespace).
+      and_return(true)
+      expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original
+      expect_any_instance_of(Repository).to receive(:branch_names).and_call_original
+      expect_any_instance_of(Repository).to receive(:branch_count).and_call_original
+      expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original
+
+      subject.perform(project.id)
+    end
+  end
+end