diff --git a/.rubocop.yml b/.rubocop.yml
index 89aa0591c3135ce26db65e2ef769c74b2b0812ff..71273ce6098f7e2f2aa88b470b91b4edd32882be 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1,1041 +1,1061 @@
+AllCops:
+  TargetRubyVersion: 2.1
+  # Cop names are not displayed in offense messages by default. Change behavior
+  # by overriding DisplayCopNames, or by giving the -D/--display-cop-names
+  # option.
+  DisplayCopNames: true
+  # Style guide URLs are not displayed in offense messages by default. Change
+  # behavior by overriding DisplayStyleGuide, or by giving the
+  # -S/--display-style-guide option.
+  DisplayStyleGuide: false
+  # Exclude some GitLab files
+  Exclude:
+    - 'vendor/**/*'
+    - 'db/**/*'
+    - 'tmp/**/*'
+    - 'bin/**/*'
+    - 'lib/backup/**/*'
+    - 'lib/ci/backup/**/*'
+    - 'lib/tasks/**/*'
+    - 'lib/ci/migrate/**/*'
+    - 'lib/email_validator.rb'
+    - 'lib/gitlab/upgrader.rb'
+    - 'lib/gitlab/seeder.rb'
+
+
+##################### Style ##################################
+
+# Check indentation of private/protected visibility modifiers.
 Style/AccessModifierIndentation:
-  Description: Check indentation of private/protected visibility modifiers.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-public-private-protected'
   Enabled: true
 
+# Check the naming of accessor methods for get_/set_.
 Style/AccessorMethodName:
-  Description: Check the naming of accessor methods for get_/set_.
   Enabled: false
 
+# Use alias_method instead of alias.
 Style/Alias:
-  Description: 'Use alias_method instead of alias.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#alias-method'
+  EnforcedStyle: prefer_alias_method
   Enabled: true
 
+# Align the elements of an array literal if they span more than one line.
 Style/AlignArray:
-  Description: >-
-                 Align the elements of an array literal if they span more than
-                 one line.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#align-multiline-arrays'
   Enabled: true
 
+# Align the elements of a hash literal if they span more than one line.
 Style/AlignHash:
-  Description: >-
-                 Align the elements of a hash literal if they span more than
-                 one line.
   Enabled: true
 
+# Align the parameters of a method call if they span more than one line.
 Style/AlignParameters:
-  Description: >-
-                 Align the parameters of a method call if they span more
-                 than one line.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-double-indent'
   Enabled: false
 
+# Use &&/|| instead of and/or.
 Style/AndOr:
-  Description: 'Use &&/|| instead of and/or.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-and-or-or'
   Enabled: false
 
+# Use `Array#join` instead of `Array#*`.
 Style/ArrayJoin:
-  Description: 'Use Array#join instead of Array#*.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#array-join'
   Enabled: false
 
+# Use only ascii symbols in comments.
 Style/AsciiComments:
-  Description: 'Use only ascii symbols in comments.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-comments'
   Enabled: true
 
+# Use only ascii symbols in identifiers.
 Style/AsciiIdentifiers:
-  Description: 'Use only ascii symbols in identifiers.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-identifiers'
   Enabled: true
 
+# Checks for uses of Module#attr.
 Style/Attr:
-  Description: 'Checks for uses of Module#attr.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr'
   Enabled: false
 
+# Avoid the use of BEGIN blocks.
 Style/BeginBlock:
-  Description: 'Avoid the use of BEGIN blocks.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-BEGIN-blocks'
   Enabled: true
 
+# Checks if usage of %() or %Q() matches configuration.
 Style/BarePercentLiterals:
-  Description: 'Checks if usage of %() or %Q() matches configuration.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q-shorthand'
   Enabled: false
 
+# Do not use block comments.
 Style/BlockComments:
-  Description: 'Do not use block comments.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-block-comments'
   Enabled: false
 
+# Put end statement of multiline block on its own line.
 Style/BlockEndNewline:
-  Description: 'Put end statement of multiline block on its own line.'
   Enabled: true
 
+# Avoid using {...} for multi-line blocks (multiline chaining is # always
+# ugly). Prefer {...} over do...end for single-line blocks.
 Style/BlockDelimiters:
-  Description: >-
-                Avoid using {...} for multi-line blocks (multiline chaining is
-                always ugly).
-                Prefer {...} over do...end for single-line blocks.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks'
   Enabled: true
 
+# Enforce braces style around hash parameters.
 Style/BracesAroundHashParameters:
-  Description: 'Enforce braces style around hash parameters.'
   Enabled: false
 
+# Avoid explicit use of the case equality operator(===).
 Style/CaseEquality:
-  Description: 'Avoid explicit use of the case equality operator(===).'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-case-equality'
   Enabled: false
 
+# Indentation of when in a case/when/[else/]end.
 Style/CaseIndentation:
-  Description: 'Indentation of when in a case/when/[else/]end.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-when-to-case'
   Enabled: true
 
+# Checks for uses of character literals.
 Style/CharacterLiteral:
-  Description: 'Checks for uses of character literals.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-character-literals'
   Enabled: true
 
+# Use CamelCase for classes and modules.'
 Style/ClassAndModuleCamelCase:
-  Description: 'Use CamelCase for classes and modules.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#camelcase-classes'
   Enabled: true
 
+# Checks style of children classes and modules.
 Style/ClassAndModuleChildren:
-  Description: 'Checks style of children classes and modules.'
   Enabled: false
 
+# Enforces consistent use of `Object#is_a?` or `Object#kind_of?`.
 Style/ClassCheck:
-  Description: 'Enforces consistent use of `Object#is_a?` or `Object#kind_of?`.'
   Enabled: false
 
+# Use self when defining module/class methods.
 Style/ClassMethods:
-  Description: 'Use self when defining module/class methods.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#def-self-singletons'
   Enabled: false
 
+# Avoid the use of class variables.
 Style/ClassVars:
-  Description: 'Avoid the use of class variables.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-class-vars'
   Enabled: true
 
+# Do not use :: for method call.
 Style/ColonMethodCall:
-  Description: 'Do not use :: for method call.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#double-colons'
   Enabled: false
 
+# Checks formatting of special comments (TODO, FIXME, OPTIMIZE, HACK, REVIEW).
 Style/CommentAnnotation:
-  Description: >-
-                 Checks formatting of special comments
-                 (TODO, FIXME, OPTIMIZE, HACK, REVIEW).
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#annotate-keywords'
   Enabled: false
 
+# Indentation of comments.
 Style/CommentIndentation:
-  Description: 'Indentation of comments.'
   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:
-  Description: 'Constants should use SCREAMING_SNAKE_CASE.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#screaming-snake-case'
   Enabled: true
 
+# Use def with parentheses when there are arguments.
 Style/DefWithParentheses:
-  Description: 'Use def with parentheses when there are arguments.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens'
   Enabled: false
 
+# Checks for use of deprecated Hash methods.
 Style/DeprecatedHashMethods:
-  Description: 'Checks for use of deprecated Hash methods.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-key'
   Enabled: false
 
+# Document classes and non-namespace modules.
 Style/Documentation:
-  Description: 'Document classes and non-namespace modules.'
   Enabled: false
 
+# Checks the position of the dot in multi-line method calls.
 Style/DotPosition:
-  Description: 'Checks the position of the dot in multi-line method calls.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains'
   Enabled: false
 
+# Checks for uses of double negation (!!).
 Style/DoubleNegation:
-  Description: 'Checks for uses of double negation (!!).'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-bang-bang'
   Enabled: false
 
+# Prefer `each_with_object` over `inject` or `reduce`.
 Style/EachWithObject:
-  Description: 'Prefer `each_with_object` over `inject` or `reduce`.'
   Enabled: false
 
+# Align elses and elsifs correctly.
 Style/ElseAlignment:
-  Description: 'Align elses and elsifs correctly.'
   Enabled: true
 
+# Avoid empty else-clauses.
 Style/EmptyElse:
-  Description: 'Avoid empty else-clauses.'
   Enabled: false
 
+# Use empty lines between defs.
 Style/EmptyLineBetweenDefs:
-  Description: 'Use empty lines between defs.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#empty-lines-between-methods'
   Enabled: false
 
+# Don't use several empty lines in a row.
 Style/EmptyLines:
-  Description: "Don't use several empty lines in a row."
   Enabled: false
 
+# Keep blank lines around access modifiers.
 Style/EmptyLinesAroundAccessModifier:
-  Description: "Keep blank lines around access modifiers."
   Enabled: false
 
+# Keeps track of empty lines around block bodies.
 Style/EmptyLinesAroundBlockBody:
-  Description: "Keeps track of empty lines around block bodies."
   Enabled: false
 
+# Keeps track of empty lines around class bodies.
 Style/EmptyLinesAroundClassBody:
-  Description: "Keeps track of empty lines around class bodies."
   Enabled: false
 
+# Keeps track of empty lines around module bodies.
 Style/EmptyLinesAroundModuleBody:
-  Description: "Keeps track of empty lines around module bodies."
   Enabled: false
 
+# Keeps track of empty lines around method bodies.
 Style/EmptyLinesAroundMethodBody:
-  Description: "Keeps track of empty lines around method bodies."
   Enabled: false
 
+# Prefer literals to Array.new/Hash.new/String.new.
 Style/EmptyLiteral:
-  Description: 'Prefer literals to Array.new/Hash.new/String.new.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#literal-array-hash'
   Enabled: false
 
+# Avoid the use of END blocks.
 Style/EndBlock:
-  Description: 'Avoid the use of END blocks.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-END-blocks'
   Enabled: false
 
+# Use Unix-style line endings.
 Style/EndOfLine:
-  Description: 'Use Unix-style line endings.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#crlf'
   Enabled: false
 
+# Favor the use of Fixnum#even? && Fixnum#odd?
 Style/EvenOdd:
-  Description: 'Favor the use of Fixnum#even? && Fixnum#odd?'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods'
   Enabled: false
 
+# Do not use unnecessary spacing.
 Style/ExtraSpacing:
-  Description: 'Do not use unnecessary spacing.'
   Enabled: false
 
+# Use snake_case for source file names.
 Style/FileName:
-  Description: 'Use snake_case for source file names.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-files'
   Enabled: false
 
+# Checks for flip flops.
 Style/FlipFlop:
-  Description: 'Checks for flip flops'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-flip-flops'
   Enabled: false
 
+# Checks use of for or each in multiline loops.
 Style/For:
-  Description: 'Checks use of for or each in multiline loops.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-for-loops'
   Enabled: false
 
+# Enforce the use of Kernel#sprintf, Kernel#format or String#%.
 Style/FormatString:
-  Description: 'Enforce the use of Kernel#sprintf, Kernel#format or String#%.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#sprintf'
   Enabled: false
 
+# Do not introduce global variables.
 Style/GlobalVars:
-  Description: 'Do not introduce global variables.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#instance-vars'
   Enabled: false
 
+# Check for conditionals that can be replaced with guard clauses.
 Style/GuardClause:
-  Description: 'Check for conditionals that can be replaced with guard clauses'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals'
   Enabled: false
 
+# Prefer Ruby 1.9 hash syntax `{ a: 1, b: 2 }`
+# over 1.8 syntax `{ :a => 1, :b => 2 }`.
 Style/HashSyntax:
-  Description: >-
-                 Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax
-                 { :a => 1, :b => 2 }.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-literals'
   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:
-  Description: >-
-                 Favor modifier if/unless usage when you have a
-                 single-line body.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier'
   Enabled: false
 
+# Do not use if x; .... Use the ternary operator instead.
 Style/IfWithSemicolon:
-  Description: 'Do not use if x; .... Use the ternary operator instead.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon-ifs'
   Enabled: false
 
+# Checks that conditional statements do not have an identical line at the
+# end of each branch, which can validly be moved out of the conditional.
+Style/IdenticalConditionalBranches:
+  Enabled: false
+
+# Checks the indentation of the first line of the right-hand-side of a
+# multi-line assignment. 
+Style/IndentAssignment:
+  Enabled: false
+
+# Keep indentation straight.
 Style/IndentationConsistency:
-  Description: 'Keep indentation straight.'
   Enabled: true
 
+# Use 2 spaces for indentation.
 Style/IndentationWidth:
-  Description: 'Use 2 spaces for indentation.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation'
   Enabled: true
 
+# Checks the indentation of the first element in an array literal.
 Style/IndentArray:
-  Description: >-
-                 Checks the indentation of the first element in an array
-                 literal.
   Enabled: false
 
+# Checks the indentation of the first key in a hash literal.
 Style/IndentHash:
-  Description: 'Checks the indentation of the first key in a hash literal.'
   Enabled: false
 
+# Use Kernel#loop for infinite loops.
 Style/InfiniteLoop:
-  Description: 'Use Kernel#loop for infinite loops.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#infinite-loop'
   Enabled: false
 
+# Use the new lambda literal syntax for single-line blocks.
 Style/Lambda:
-  Description: 'Use the new lambda literal syntax for single-line blocks.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#lambda-multi-line'
   Enabled: false
 
+# Use lambda.call(...) instead of lambda.(...).
 Style/LambdaCall:
-  Description: 'Use lambda.call(...) instead of lambda.(...).'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc-call'
   Enabled: false
 
+# Comments should start with a space.
 Style/LeadingCommentSpace:
-  Description: 'Comments should start with a space.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-space'
   Enabled: false
 
+# Use \ instead of + or << to concatenate two string literals at line end.
 Style/LineEndConcatenation:
-  Description: >-
-                 Use \ instead of + or << to concatenate two string literals at
-                 line end.
   Enabled: false
 
+# Do not use parentheses for method calls with no arguments.
 Style/MethodCallParentheses:
-  Description: 'Do not use parentheses for method calls with no arguments.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-args-no-parens'
   Enabled: false
 
+# Checks if the method definitions have or don't have parentheses.
 Style/MethodDefParentheses:
-  Description: >-
-                 Checks if the method definitions have or don't have
-                 parentheses.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens'
   Enabled: false
 
+# Use the configured style when naming methods.
 Style/MethodName:
-  Description: 'Use the configured style when naming methods.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars'
   Enabled: false
 
+# Checks for usage of `extend self` in modules.
 Style/ModuleFunction:
-  Description: 'Checks for usage of `extend self` in modules.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#module-function'
   Enabled: false
 
+# Avoid multi-line chains of blocks.
 Style/MultilineBlockChain:
-  Description: 'Avoid multi-line chains of blocks.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks'
   Enabled: false
 
+# Ensures newlines after multiline block do statements.
 Style/MultilineBlockLayout:
-  Description: 'Ensures newlines after multiline block do statements.'
   Enabled: true
 
+# Do not use then for multi-line if/unless.
 Style/MultilineIfThen:
-  Description: 'Do not use then for multi-line if/unless.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-then'
   Enabled: false
 
+# Checks indentation of method calls with the dot operator that span more than
+# one line.
+Style/MultilineMethodCallIndentation:
+  Enabled: false
+
+# Checks indentation of binary operations that span more than one line.
 Style/MultilineOperationIndentation:
-  Description: >-
-                 Checks indentation of binary operations that span more than
-                 one line.
   Enabled: false
 
+# Avoid multi-line `? :` (the ternary operator), use if/unless instead.
 Style/MultilineTernaryOperator:
-  Description: >-
-                 Avoid multi-line ?: (the ternary operator);
-                 use if/unless instead.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-ternary'
   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:
-  Description: >-
-                 Favor unless over if for negative conditions
-                 (or control flow or).
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#unless-for-negatives'
   Enabled: false
 
+# Favor until over while for negative conditions.
 Style/NegatedWhile:
-  Description: 'Favor until over while for negative conditions.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#until-for-negatives'
   Enabled: false
 
+# Avoid using nested modifiers.
+Style/NestedModifier:
+  Enabled: false
+
+# 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:
-  Description: 'Use one expression per branch in a ternary operator.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-ternary'
   Enabled: true
 
+# Use `next` to skip iteration instead of a condition at the end.
 Style/Next:
-  Description: 'Use `next` to skip iteration instead of a condition at the end.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals'
   Enabled: false
 
+# Prefer x.nil? to x == nil.
 Style/NilComparison:
-  Description: 'Prefer x.nil? to x == nil.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods'
   Enabled: true
 
+# Checks for redundant nil checks.
 Style/NonNilCheck:
-  Description: 'Checks for redundant nil checks.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-non-nil-checks'
   Enabled: true
 
+# Use ! instead of not.
 Style/Not:
-  Description: 'Use ! instead of not.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bang-not-not'
   Enabled: true
 
+# Add underscores to large numeric literals to improve their readability.
 Style/NumericLiterals:
-  Description: >-
-                 Add underscores to large numeric literals to improve their
-                 readability.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscores-in-numerics'
   Enabled: false
 
+# Favor the ternary operator(?:) over if/then/else/end constructs.
 Style/OneLineConditional:
-  Description: >-
-                 Favor the ternary operator(?:) over
-                 if/then/else/end constructs.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#ternary-operator'
   Enabled: true
 
+# When defining binary operators, name the argument other.
 Style/OpMethod:
-  Description: 'When defining binary operators, name the argument other.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#other-arg'
   Enabled: false
 
+# 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:
-  Description: >-
-                  Check for simple usages of parallel assignment.
-                  It will only warn when the number of variables
-                  matches on both sides of the assignment.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parallel-assignment'
   Enabled: false
 
+# Don't use parentheses around the condition of an if/unless/while.
 Style/ParenthesesAroundCondition:
-  Description: >-
-                 Don't use parentheses around the condition of an
-                 if/unless/while.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-parens-if'
   Enabled: true
 
+# Use `%`-literal delimiters consistently.
 Style/PercentLiteralDelimiters:
-  Description: 'Use `%`-literal delimiters consistently'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-literal-braces'
   Enabled: false
 
+# Checks if uses of %Q/%q match the configured preference.
 Style/PercentQLiterals:
-  Description: 'Checks if uses of %Q/%q match the configured preference.'
   Enabled: false
 
+# Avoid Perl-style regex back references.
 Style/PerlBackrefs:
-  Description: 'Avoid Perl-style regex back references.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers'
   Enabled: false
 
+# Check the names of predicate methods.
 Style/PredicateName:
-  Description: 'Check the names of predicate methods.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark'
   Enabled: false
 
+# Use proc instead of Proc.new.
 Style/Proc:
-  Description: 'Use proc instead of Proc.new.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc'
   Enabled: false
 
+# Checks the arguments passed to raise/fail.
 Style/RaiseArgs:
-  Description: 'Checks the arguments passed to raise/fail.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#exception-class-messages'
   Enabled: false
 
+# Don't use begin blocks when they are not needed.
 Style/RedundantBegin:
-  Description: "Don't use begin blocks when they are not needed."
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#begin-implicit'
   Enabled: false
 
+# Checks for an obsolete RuntimeException argument in raise/fail.
 Style/RedundantException:
-  Description: "Checks for an obsolete RuntimeException argument in raise/fail."
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-runtimeerror'
   Enabled: false
 
+# Checks usages of Object#freeze on immutable objects.
+Style/RedundantFreeze:
+  Enabled: false
+
+# TODO: Enable RedundantParentheses Cop.
+# Checks for parentheses that seem not to serve any purpose.
+Style/RedundantParentheses:
+  Enabled: false
+
+# Don't use return where it's not required.
 Style/RedundantReturn:
-  Description: "Don't use return where it's not required."
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-return'
   Enabled: true
 
+# Don't use self where it's not needed.
 Style/RedundantSelf:
-  Description: "Don't use self where it's not needed."
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-self-unless-required'
   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:
-  Description: >-
-                 Use %r for regular expressions matching more than
-                 `MaxSlashes` '/' characters.
-                 Use %r only for regular expressions matching more than
-                 `MaxSlashes` '/' character.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-r'
   Enabled: false
 
+# Avoid using rescue in its modifier form.
 Style/RescueModifier:
-  Description: 'Avoid using rescue in its modifier form.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-rescue-modifiers'
   Enabled: false
 
+# Checks for places where self-assignment shorthand should have been used.
 Style/SelfAssignment:
-  Description: >-
-                 Checks for places where self-assignment shorthand should have
-                 been used.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#self-assignment'
   Enabled: false
 
+# Don't use semicolons to terminate expressions.
 Style/Semicolon:
-  Description: "Don't use semicolons to terminate expressions."
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon'
   Enabled: false
 
+# Checks for proper usage of fail and raise.
 Style/SignalException:
-  Description: 'Checks for proper usage of fail and raise.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#fail-method'
   Enabled: false
 
+# Enforces the names of some block params.
 Style/SingleLineBlockParams:
-  Description: 'Enforces the names of some block params.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#reduce-blocks'
   Enabled: false
 
+# Avoid single-line methods.
 Style/SingleLineMethods:
-  Description: 'Avoid single-line methods.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-single-line-methods'
-  Enabled: false
-
-Style/SingleSpaceBeforeFirstArg:
-  Description: >-
-                 Checks that exactly one space is used between a method name
-                 and the first argument for method calls without parentheses.
   Enabled: false
 
+# Use spaces after colons.
 Style/SpaceAfterColon:
-  Description: 'Use spaces after colons.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators'
   Enabled: false
 
+# Use spaces after commas.
 Style/SpaceAfterComma:
-  Description: 'Use spaces after commas.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators'
-  Enabled: false
-
-Style/SpaceAfterControlKeyword:
-  Description: 'Use spaces after if/elsif/unless/while/until/case/when.'
   Enabled: false
 
+# Do not put a space between a method name and the opening parenthesis in a
+# method definition.
 Style/SpaceAfterMethodName:
-  Description: >-
-                 Do not put a space between a method name and the opening
-                 parenthesis in a method definition.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces'
   Enabled: false
 
+# Tracks redundant space after the ! operator.
 Style/SpaceAfterNot:
-  Description: Tracks redundant space after the ! operator.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-bang'
   Enabled: false
 
+# Use spaces after semicolons.
 Style/SpaceAfterSemicolon:
-  Description: 'Use spaces after semicolons.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators'
   Enabled: false
 
-Style/SpaceBeforeBlockBraces:
-  Description: >-
-                 Checks that the left block brace has or doesn't have space
-                 before it.
+# Checks that the equals signs in parameter default assignments have or don't
+# have surrounding space depending on configuration.
+Style/SpaceAroundEqualsInParameterDefault:
   Enabled: false
 
-Style/SpaceBeforeComma:
-  Description: 'No spaces before commas.'
+# TODO: Enable SpaceAroundKeyword Cop.
+# Use a space around keywords if appropriate.
+Style/SpaceAroundKeyword:
   Enabled: false
 
-Style/SpaceBeforeComment:
-  Description: >-
-                 Checks for missing space between code and a comment on the
-                 same line.
+# Use a single space around operators.
+Style/SpaceAroundOperators:
   Enabled: false
 
-Style/SpaceBeforeSemicolon:
-  Description: 'No spaces before semicolons.'
+# Checks that the left block brace has or doesn't have space before it.
+Style/SpaceBeforeBlockBraces:
   Enabled: false
 
-Style/SpaceInsideBlockBraces:
-  Description: >-
-                 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.
+# No spaces before commas.
+Style/SpaceBeforeComma:
   Enabled: false
 
-Style/SpaceAroundEqualsInParameterDefault:
-  Description: >-
-                 Checks that the equals signs in parameter default assignments
-                 have or don't have surrounding space depending on
-                 configuration.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-around-equals'
+# Checks for missing space between code and a comment on the same line.
+Style/SpaceBeforeComment:
   Enabled: false
 
-Style/SpaceAroundOperators:
-  Description: 'Use spaces around operators.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators'
+# 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: false
 
-Style/SpaceBeforeModifierKeyword:
-  Description: 'Put a space before the modifier keyword.'
+# 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:
-  Description: 'No spaces after [ or before ].'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces'
   Enabled: false
 
+# Use spaces inside hash literal braces - or don't.
 Style/SpaceInsideHashLiteralBraces:
-  Description: "Use spaces inside hash literal braces - or don't."
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators'
   Enabled: true
 
+# No spaces after ( or before ).
 Style/SpaceInsideParens:
-  Description: 'No spaces after ( or before ).'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces'
   Enabled: false
 
+# No spaces inside range literals.
 Style/SpaceInsideRangeLiteral:
-  Description: 'No spaces inside range literals.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-inside-range-literals'
   Enabled: false
 
+# Checks for padding/surrounding spaces inside string interpolation.
+Style/SpaceInsideStringInterpolation:
+  Enabled: false
+
+# Avoid Perl-style global variables.
 Style/SpecialGlobalVars:
-  Description: 'Avoid Perl-style global variables.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms'
   Enabled: false
 
+# Check for the usage of parentheses around stabby lambda arguments.
+Style/StabbyLambdaParentheses:
+  Enabled: false
+
+# Checks if uses of quotes match the configured preference.
 Style/StringLiterals:
-  Description: 'Checks if uses of quotes match the configured preference.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-string-literals'
   Enabled: false
 
+# Checks if uses of quotes inside expressions in interpolated strings match the
+# configured preference.
 Style/StringLiteralsInInterpolation:
-  Description: >-
-                 Checks if uses of quotes inside expressions in interpolated
-                 strings match the configured preference.
   Enabled: false
 
+# Checks if configured preferred methods are used over non-preferred.
+Style/StringMethods:
+  Enabled: false
+
+# Use %i or %I for arrays of symbols.
+Style/SymbolArray:
+  Enabled: false
+
+# Use symbols as procs instead of blocks when possible.
 Style/SymbolProc:
-  Description: 'Use symbols as procs instead of blocks when possible.'
   Enabled: false
 
+# No hard tabs.
 Style/Tab:
-  Description: 'No hard tabs.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation'
   Enabled: true
 
+# Checks trailing blank lines and final newline.
 Style/TrailingBlankLines:
-  Description: 'Checks trailing blank lines and final newline.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#newline-eof'
   Enabled: true
 
-Style/TrailingComma:
-  Description: 'Checks for trailing comma in parameter lists and literals.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas'
+# 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:
-  Description: 'Avoid trailing whitespace.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace'
   Enabled: false
 
+# Checks for the usage of unneeded trailing underscores at the end of
+# parallel variable assignment.
 Style/TrailingUnderscoreVariable:
-  Description: >-
-                 Checks for the usage of unneeded trailing underscores at the
-                 end of parallel variable assignment.
-  AllowNamedUnderscoreVariables: true
   Enabled: false
 
+# Prefer attr_* methods to trivial readers/writers.
 Style/TrivialAccessors:
-  Description: 'Prefer attr_* methods to trivial readers/writers.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr_family'
   Enabled: false
 
+# Do not use unless with else. Rewrite these with the positive case first.
 Style/UnlessElse:
-  Description: >-
-                 Do not use unless with else. Rewrite these with the positive
-                 case first.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-else-with-unless'
   Enabled: false
 
+# Checks for %W when interpolation is not needed.
 Style/UnneededCapitalW:
-  Description: 'Checks for %W when interpolation is not needed.'
   Enabled: false
 
+# 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:
-  Description: 'Checks for %q/%Q when single quotes or double quotes would do.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q'
   Enabled: false
 
+# Don't interpolate global, instance and class variables directly in strings.
 Style/VariableInterpolation:
-  Description: >-
-                 Don't interpolate global, instance and class variables
-                 directly in strings.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#curlies-interpolate'
   Enabled: false
 
+# Use the configured style when naming variables.
 Style/VariableName:
-  Description: 'Use the configured style when naming variables.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars'
   Enabled: false
 
+# Use when x then ... for one-line cases.
 Style/WhenThen:
-  Description: 'Use when x then ... for one-line cases.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#one-line-cases'
   Enabled: false
 
+# Checks for redundant do after while or until.
 Style/WhileUntilDo:
-  Description: 'Checks for redundant do after while or until.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-while-do'
   Enabled: false
 
+# Favor modifier while/until usage when you have a single-line body.
 Style/WhileUntilModifier:
-  Description: >-
-                 Favor modifier while/until usage when you have a
-                 single-line body.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#while-as-a-modifier'
   Enabled: false
 
+# Use %w or %W for arrays of words.
 Style/WordArray:
-  Description: 'Use %w or %W for arrays of words.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-w'
   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,
+# branches, and conditions.
 Metrics/AbcSize:
-  Description: >-
-                 A calculated magnitude based on number of assignments,
-                 branches, and conditions.
   Enabled: true
   Max: 70
 
-Metrics/CyclomaticComplexity:
-  Description: >-
-                 A complexity metric that is strongly correlated to the number
-                 of test cases needed to validate a method.
-  Enabled: true
-  Max: 17
-
-Metrics/PerceivedComplexity:
-  Description: >-
-                 A complexity metric geared towards measuring complexity for a
-                 human reader.
-  Enabled: true
-  Max: 17
-
-Metrics/ParameterLists:
-  Description: 'Avoid parameter lists longer than three or four parameters.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#too-many-params'
-  Enabled: true
-  Max: 8
-
+# Avoid excessive block nesting.
 Metrics/BlockNesting:
-  Description: 'Avoid excessive block nesting'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count'
   Enabled: true
   Max: 4
 
+# Avoid classes longer than 100 lines of code.
 Metrics/ClassLength:
-  Description: 'Avoid classes longer than 100 lines of code.'
   Enabled: false
 
+# A complexity metric that is strongly correlated to the number
+# of test cases needed to validate a method.
+Metrics/CyclomaticComplexity:
+  Enabled: true
+  Max: 17
+
+# Limit lines to 80 characters.
 Metrics/LineLength:
-  Description: 'Limit lines to 80 characters.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#80-character-limits'
   Enabled: false
 
+# Avoid methods longer than 10 lines of code.
 Metrics/MethodLength:
-  Description: 'Avoid methods longer than 10 lines of code.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods'
   Enabled: false
 
+# Avoid modules longer than 100 lines of code.
 Metrics/ModuleLength:
-  Description: 'Avoid modules longer than 100 lines of code.'
   Enabled: false
 
+# Avoid parameter lists longer than three or four parameters.
+Metrics/ParameterLists:
+  Enabled: true
+  Max: 8
+
+# A complexity metric geared towards measuring complexity for a human reader.
+Metrics/PerceivedComplexity:
+  Enabled: true
+  Max: 17
+
+
 #################### Lint ################################
-### Warnings
 
+# Checks for ambiguous operators in the first argument of a method invocation
+# without parentheses.
 Lint/AmbiguousOperator:
-  Description: >-
-                 Checks for ambiguous operators in the first argument of a
-                 method invocation without parentheses.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-as-args'
   Enabled: false
 
+# Checks for ambiguous regexp literals in the first argument of a method
+# invocation without parentheses.
 Lint/AmbiguousRegexpLiteral:
-  Description: >-
-                 Checks for ambiguous regexp literals in the first argument of
-                 a method invocation without parenthesis.
   Enabled: false
 
+# Don't use assignment in conditions.
 Lint/AssignmentInCondition:
-  Description: "Don't use assignment in conditions."
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition'
   Enabled: false
 
+# Align block ends correctly.
 Lint/BlockAlignment:
-  Description: 'Align block ends correctly.'
   Enabled: false
 
+# Default values in optional keyword arguments and optional ordinal arguments
+# should not refer back to the name of the argument.
+Lint/CircularArgumentReference:
+  Enabled: false
+
+# Checks for condition placed in a confusing position relative to the keyword.
 Lint/ConditionPosition:
-  Description: >-
-                 Checks for condition placed in a confusing position relative to
-                 the keyword.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#same-line-condition'
   Enabled: false
 
+# Check for debugger calls.
 Lint/Debugger:
-  Description: 'Check for debugger calls.'
   Enabled: false
 
+# Align ends corresponding to defs correctly.
 Lint/DefEndAlignment:
-  Description: 'Align ends corresponding to defs correctly.'
   Enabled: false
 
+# Check for deprecated class method calls.
 Lint/DeprecatedClassMethods:
-  Description: 'Check for deprecated class method calls.'
   Enabled: false
 
+# 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: false
+
+# Check for odd code arrangement in an else block.
 Lint/ElseLayout:
-  Description: 'Check for odd code arrangement in an else block.'
   Enabled: false
 
+# Checks for empty ensure block.
 Lint/EmptyEnsure:
-  Description: 'Checks for empty ensure block.'
   Enabled: false
 
+# Checks for empty string interpolation.
 Lint/EmptyInterpolation:
-  Description: 'Checks for empty string interpolation.'
   Enabled: false
 
+# Align ends correctly.
 Lint/EndAlignment:
-  Description: 'Align ends correctly.'
   Enabled: false
 
+# END blocks should not be placed inside method definitions.
 Lint/EndInMethod:
-  Description: 'END blocks should not be placed inside method definitions.'
   Enabled: false
 
+# Do not use return in an ensure block.
 Lint/EnsureReturn:
-  Description: 'Do not use return in an ensure block.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-return-ensure'
   Enabled: false
 
+# The use of eval represents a serious security risk.
 Lint/Eval:
-  Description: 'The use of eval represents a serious security risk.'
   Enabled: false
 
+# Catches floating-point literals too large or small for Ruby to represent.
+Lint/FloatOutOfRange:
+  Enabled: false
+
+# The number of parameters to format/sprint must match the fields.
+Lint/FormatParameterMismatch:
+  Enabled: false
+
+# Don't suppress exception.
 Lint/HandleExceptions:
-  Description: "Don't suppress exception."
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions'
   Enabled: false
 
+# TODO: Enable ImplicitStringConcatenation Cop.
+# Checks for adjacent string literals on the same line, which could better be
+# represented as a single string literal.
+Lint/ImplicitStringConcatenation:
+  Enabled: false
+
+# 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:
-  Description: >-
-                 Checks for invalid character literals with a non-escaped
-                 whitespace character.
   Enabled: false
 
+# Checks of literals used in conditions.
 Lint/LiteralInCondition:
-  Description: 'Checks of literals used in conditions.'
   Enabled: false
 
+# Checks for literals used in interpolation.
 Lint/LiteralInInterpolation:
-  Description: 'Checks for literals used in interpolation.'
   Enabled: false
 
+# Use Kernel#loop with break rather than begin/end/until or begin/end/while
+# for post-loop tests.
 Lint/Loop:
-  Description: >-
-                 Use Kernel#loop with break rather than begin/end/until or
-                 begin/end/while for post-loop tests.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#loop-with-break'
   Enabled: false
 
+# Do not use nested method definitions.
+Lint/NestedMethodDefinition:
+  Enabled: false
+
+# Do not omit the accumulator when calling `next` in a `reduce`/`inject` block.
+Lint/NextWithoutAccumulator:
+  Enabled: false
+
+# Checks for method calls with a space before the opening parenthesis.
 Lint/ParenthesesAsGroupedExpression:
-  Description: >-
-                 Checks for method calls with a space before the opening
-                 parenthesis.
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces'
   Enabled: true
 
+# Checks for `rand(1)` calls. Such calls always return `0` and most likely
+# a mistake.
+Lint/RandOne:
+  Enabled: false
+
+# Use parentheses in the method call to avoid confusion about precedence.
 Lint/RequireParentheses:
-  Description: >-
-                 Use parentheses in the method call to avoid confusion
-                 about precedence.
   Enabled: false
 
+# Avoid rescuing the Exception class.
 Lint/RescueException:
-  Description: 'Avoid rescuing the Exception class.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-blind-rescues'
   Enabled: true
 
+# Do not use the same name as outer local variable for block arguments
+# or block local variables.
 Lint/ShadowingOuterLocalVariable:
-  Description: >-
-                 Do not use the same name as outer local variable
-                 for block arguments or block local variables.
-  Enabled: false
-
-Lint/SpaceBeforeFirstArg:
-  Description: >-
-                 Put a space between a method name and the first argument
-                 in a method call without parentheses.
   Enabled: false
 
+# 'Checks for Object#to_s usage in string interpolation.
 Lint/StringConversionInInterpolation:
-  Description: 'Checks for Object#to_s usage in string interpolation.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-to-s'
   Enabled: false
 
+# Do not use prefix `_` for a variable that is used.
 Lint/UnderscorePrefixedVariableName:
-  Description: 'Do not use prefix `_` for a variable that is used.'
   Enabled: true
 
+# Checks for rubocop:disable comments that can be removed.
+# Note: this cop is not disabled when disabling all cops.
+# It must be explicitly disabled.
+Lint/UnneededDisable:
+  Enabled: false
+
+# Checks for unused block arguments.
 Lint/UnusedBlockArgument:
-  Description: 'Checks for unused block arguments.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars'
   Enabled: false
 
+# Checks for unused method arguments.
 Lint/UnusedMethodArgument:
-  Description: 'Checks for unused method arguments.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars'
   Enabled: false
 
+# Unreachable code.
 Lint/UnreachableCode:
-  Description: 'Unreachable code.'
   Enabled: false
 
+# Checks for useless access modifiers.
 Lint/UselessAccessModifier:
-  Description: 'Checks for useless access modifiers.'
   Enabled: false
 
+# Checks for useless assignment to a local variable.
 Lint/UselessAssignment:
-  Description: 'Checks for useless assignment to a local variable.'
-  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars'
   Enabled: true
 
+# Checks for comparison of something with itself.
 Lint/UselessComparison:
-  Description: 'Checks for comparison of something with itself.'
   Enabled: false
 
+# Checks for useless `else` in `begin..end` without `rescue`.
 Lint/UselessElseWithoutRescue:
-  Description: 'Checks for useless `else` in `begin..end` without `rescue`.'
   Enabled: false
 
+# Checks for useless setter call to a local variable.
 Lint/UselessSetterCall:
-  Description: 'Checks for useless setter call to a local variable.'
   Enabled: false
 
+# Possible use of operator/literal/variable in void context.
 Lint/Void:
-  Description: 'Possible use of operator/literal/variable in void context.'
   Enabled: false
 
+
+##################### Performance ############################
+
+# TODO: Enable Casecmp Cop.
+# Use `casecmp` rather than `downcase ==`.
+Performance/Casecmp:
+  Enabled: false
+
+# TODO: Enable DoubleStartEndWith Cop.
+# Use `str.{start,end}_with?(x, ..., y, ...)` instead of
+# `str.{start,end}_with?(x, ...) || str.{start,end}_with?(y, ...)`.
+Performance/DoubleStartEndWith:
+  Enabled: false
+
+# TODO: Enable EndWith Cop.
+# Use `end_with?` instead of a regex match anchored to the end of a string.
+Performance/EndWith:
+  Enabled: false
+
+# TODO: Enable LstripRstrip Cop.
+# Use `strip` instead of `lstrip.rstrip`.
+Performance/LstripRstrip:
+  Enabled: false
+
+# TODO: Enable RangeInclude Cop.
+# Use `Range#cover?` instead of `Range#include?`.
+Performance/RangeInclude:
+  Enabled: false
+
+# 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
+
+# TODO: Enable RedundantSortBy Cop.
+# Use `sort` instead of `sort_by { |x| x }`.
+Performance/RedundantSortBy:
+  Enabled: false
+
+# TODO: Enable StartWith Cop.
+# Use `start_with?` instead of a regex match anchored to the beginning of a
+# string.
+Performance/StartWith:
+  Enabled: false
+# Use `tr` instead of `gsub` when you are replacing the same number of
+# characters. Use `delete` instead of `gsub` when you are deleting
+# characters.
+Performance/StringReplacement:
+  Enabled: false
+
+# TODO: Enable TimesMap Cop.
+# Checks for `.times.map` calls.
+Performance/TimesMap:
+  Enabled: false
+
+
 ##################### Rails ##################################
 
+# Enables Rails cops.
+Rails:
+  Enabled: true
+
+# Enforces consistent use of action filter methods.
 Rails/ActionFilter:
-  Description: 'Enforces consistent use of action filter methods.'
   Enabled: true
+  EnforcedStyle: action
 
+# Checks the correct usage of date aware methods, such as `Date.today`,
+# `Date.current`, etc.
 Rails/Date:
-  Description: >-
-                  Checks the correct usage of date aware methods,
-                  such as Date.today, Date.current etc.
   Enabled: false
 
-Rails/DefaultScope:
-  Description: 'Checks if the argument passed to default_scope is a block.'
+# Prefer delegate method for delegations.
+Rails/Delegate:
   Enabled: false
 
-Rails/Delegate:
-  Description: 'Prefer delegate method for delegations.'
+# Prefer `find_by` over `where.first`.
+Rails/FindBy:
+  Enabled: false
+
+# Prefer `all.find_each` over `all.find`.
+Rails/FindEach:
   Enabled: false
 
+# Prefer has_many :through to has_and_belongs_to_many.
 Rails/HasAndBelongsToMany:
-  Description: 'Prefer has_many :through to has_and_belongs_to_many.'
   Enabled: true
 
+# Checks for calls to puts, print, etc.
 Rails/Output:
-  Description: 'Checks for calls to puts, print, etc.'
   Enabled: true
 
+# Checks for incorrect grammar when using methods like `3.day.ago`.
+Rails/PluralizationGrammar:
+  Enabled: false
+
+# Checks for `read_attribute(:attr)` and `write_attribute(:attr, val)`.
 Rails/ReadWriteAttribute:
-  Description: >-
-                 Checks for read_attribute(:attr) and
-                 write_attribute(:attr, val).
   Enabled: false
 
+# Checks the arguments of ActiveRecord scopes.
 Rails/ScopeArgs:
-  Description: 'Checks the arguments of ActiveRecord scopes.'
   Enabled: false
 
+# Checks the correct usage of time zone aware methods.
+# http://danilenko.org/2012/7/6/rails_timezones
 Rails/TimeZone:
-  Description: 'Checks the correct usage of time zone aware methods.'
-  StyleGuide: 'https://github.com/bbatsov/rails-style-guide#time'
-  Reference: 'http://danilenko.org/2012/7/6/rails_timezones'
   Enabled: false
 
+# Use validates :attribute, hash of validations.
 Rails/Validation:
-  Description: 'Use validates :attribute, hash of validations.'
   Enabled: false
-
-
-# Exclude some of GitLab files
-#
-#
-AllCops:
-  RunRailsCops: true
-  Exclude:
-    - 'vendor/**/*'
-    - 'db/**/*'
-    - 'tmp/**/*'
-    - 'bin/**/*'
-    - 'lib/backup/**/*'
-    - 'lib/ci/backup/**/*'
-    - 'lib/tasks/**/*'
-    - 'lib/ci/migrate/**/*'
-    - 'lib/email_validator.rb'
-    - 'lib/gitlab/upgrader.rb'
-    - 'lib/gitlab/seeder.rb'
diff --git a/CHANGELOG b/CHANGELOG
index 72e91a1dac144dd02d7d70488e8f7d4f150548e0..9a102b8f7ec6b4d54d98d3cef6f2918e06e95fb4 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,23 +1,40 @@
 Please view this file on the master branch, on stable branches it's out of date.
 
-v 8.6.0 (unreleased)
+v 8.7.0 (unreleased)
+  - Make HTTP(s) label consistent on clone bar (Stan Hu)
+
+v 8.6.1 (unreleased)
+
+
+v 8.6.0
+  - Add ability to move issue to another project
+  - Prevent tokens in the import URL to be showed by the UI
+  - Fix bug where wrong commit ID was being used in a merge request diff to show old image (Stan Hu)
+  - Add confidential issues
   - Bump gitlab_git to 9.0.3 (Stan Hu)
+  - Fix diff image view modes (2-up, swipe, onion skin) not working (Stan Hu)
   - Support Golang subpackage fetching (Stan Hu)
   - Bump Capybara gem to 2.6.2 (Stan Hu)
   - New branch button appears on issues where applicable
   - Contributions to forked projects are included in calendar
   - Improve the formatting for the user page bio (Connor Shea)
+  - Easily (un)mark merge request as WIP using link
+  - Use specialized system notes when MR is (un)marked as WIP
   - Removed the default password from the initial admin account created during
     setup. A password can be provided during setup (see installation docs), or
     GitLab will ask the user to create a new one upon first visit.
   - Fix issue when pushing to projects ending in .wiki
+  - Properly display YAML front matter in Markdown
   - Add support for wiki with UTF-8 page names (Hiroyuki Sato)
   - Fix wiki search results point to raw source (Hiroyuki Sato)
   - Don't load all of GitLab in mail_room
+  - Add information about `image` and `services` field at `job` level in the `.gitlab-ci.yml` documentation (Pat Turner)
+  - HTTP error pages work independently from location and config (Artem Sidorenko)
   - Update `omniauth-saml` to 1.5.0 to allow for custom response attributes to be set
   - Memoize @group in Admin::GroupsController (Yatish Mehta)
   - Indicate how much an MR diverged from the target branch (Pierre de La Morinerie)
   - Added omniauth-auth0 Gem (Daniel Carraro)
+  - Add label description in tooltip to labels in issue index and sidebar
   - Strip leading and trailing spaces in URL validator (evuez)
   - Add "last_sign_in_at" and "confirmed_at" to GET /users/* API endpoints for admins (evuez)
   - Return empty array instead of 404 when commit has no statuses in commit status API
@@ -25,6 +42,7 @@ v 8.6.0 (unreleased)
   - Rewrite logo to simplify SVG code (Sean Lang)
   - Allow to use YAML anchors when parsing the `.gitlab-ci.yml` (Pascal Bach)
   - Ignore jobs that start with `.` (hidden jobs)
+  - Hide builds from project's settings when the feature is disabled
   - Allow to pass name of created artifacts archive in `.gitlab-ci.yml`
   - Refactor and greatly improve search performance
   - Add support for cross-project label references
@@ -36,15 +54,24 @@ v 8.6.0 (unreleased)
   - Fix bug where Bitbucket `closed` issues were imported as `opened` (Iuri de Silvio)
   - Don't show Issues/MRs from archived projects in Groups view
   - Fix wrong "iid of max iid" in Issuable sidebar for some merged MRs
+  - Fix empty source_sha on Merge Request when there is no diff (Pierre de La Morinerie)
   - Increase the notes polling timeout over time (Roberto Dip)
   - Add shortcut to toggle markdown preview (Florent Baldino)
   - Show labels in dashboard and group milestone views
+  - Fix an issue when the target branch of a MR had been deleted
   - Add main language of a project in the list of projects (Tiago Botelho)
+  - Add #upcoming filter to Milestone filter (Tiago Botelho)
   - Add ability to show archived projects on dashboard, explore and group pages
   - Move group activity to separate page
   - Create external users which are excluded of internal and private projects unless access was explicitly granted
   - Continue parameters are checked to ensure redirection goes to the same instance
   - User deletion is now done in the background so the request can not time out
+  - Canceled builds are now ignored in compound build status if marked as `allowed to fail`
+  - Trigger a todo for mentions on commits page
+  - Let project owners and admins soft delete issues and merge requests
+
+v 8.5.8
+  - Bump Git version requirement to 2.7.4
 
 v 8.5.7
   - Bump Git version requirement to 2.7.3
@@ -57,7 +84,6 @@ v 8.5.5
   - Prevent a 500 error in Todos when author was removed
   - Fix pagination for filtered dashboard and explore pages
   - Fix "Show all" link behavior
-  - Add #upcoming filter to Milestone filter (Tiago Botelho)
 
 v 8.5.4
   - Do not cache requests for badges (including builds badge)
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index ef5e4454454da88572cbefa4d65ef2254b9a5fe6..39e898a4f952d339c155a7939d571a5fdd6c8cfc 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-0.6.5
+0.7.1
diff --git a/Gemfile b/Gemfile
index a3fb6779e9a7f237b5a10f0000e589edb53d5419..cebd76e7370609d5f1a4333ca1f939db0b4a4e54 100644
--- a/Gemfile
+++ b/Gemfile
@@ -51,7 +51,7 @@ gem "browser", '~> 1.0.0'
 
 # Extracting information from a git repository
 # Provide access to Gitlab::Git library
-gem "gitlab_git", '~> 9.0'
+gem "gitlab_git", '~> 10.0'
 
 # LDAP Auth
 # GitLab fork with several improvements to original library. For full list of changes
@@ -222,6 +222,8 @@ gem 'net-ssh',            '~> 3.0.1'
 # Sentry integration
 gem 'sentry-raven', '~> 0.15'
 
+gem 'premailer-rails', '~> 1.9.0'
+
 # Metrics
 group :metrics do
   gem 'allocations', '~> 1.0', require: false, platform: :mri
@@ -285,7 +287,7 @@ group :development, :test do
   gem 'spring-commands-spinach',  '~> 1.0.0'
   gem 'spring-commands-teaspoon', '~> 0.0.2'
 
-  gem 'rubocop', '~> 0.35.0', require: false
+  gem 'rubocop', '~> 0.38.0', require: false
   gem 'scss_lint', '~> 0.47.0', require: false
   gem 'coveralls',  '~> 0.8.2', require: false
   gem 'simplecov', '~> 0.10.0', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index 7b0dd83da52353d13875afc830ad110dd68a653d..16c09ab6e6d849b7360eada016d31405055aeeb9 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -61,9 +61,7 @@ GEM
       faraday_middleware-multi_json (~> 0.0)
       oauth2 (~> 1.0)
     asciidoctor (1.5.3)
-    ast (2.1.0)
-    astrolabe (1.3.1)
-      parser (~> 2.2)
+    ast (2.2.0)
     attr_encrypted (1.3.4)
       encryptor (>= 1.3.0)
     attr_required (1.0.0)
@@ -150,6 +148,8 @@ GEM
     crack (0.4.3)
       safe_yaml (~> 1.0.0)
     creole (0.5.0)
+    css_parser (1.3.7)
+      addressable
     d3_rails (3.5.11)
       railties (>= 3.1.0)
     daemons (1.2.3)
@@ -359,11 +359,11 @@ GEM
       posix-spawn (~> 0.3)
     gitlab_emoji (0.3.1)
       gemojione (~> 2.2, >= 2.2.1)
-    gitlab_git (9.0.3)
+    gitlab_git (10.0.0)
       activesupport (~> 4.0)
       charlock_holmes (~> 0.7.3)
       github-linguist (~> 4.7.0)
-      rugged (~> 0.24.0b13)
+      rugged (~> 0.24.0)
     gitlab_meta (7.0)
     gitlab_omniauth-ldap (1.2.1)
       net-ldap (~> 0.9)
@@ -423,6 +423,7 @@ GEM
       haml (~> 4.0.0)
       nokogiri (~> 1.6.0)
       ruby_parser (~> 3.5)
+    htmlentities (4.3.4)
     http-cookie (1.0.2)
       domain_name (~> 0.5)
     http_parser.rb (0.5.3)
@@ -554,8 +555,8 @@ GEM
     orm_adapter (0.5.0)
     paranoia (2.1.4)
       activerecord (~> 4.0)
-    parser (2.2.3.0)
-      ast (>= 1.1, < 3.0)
+    parser (2.3.0.6)
+      ast (~> 2.2)
     pg (0.18.4)
     poltergeist (1.9.0)
       capybara (~> 2.1)
@@ -564,6 +565,12 @@ GEM
       websocket-driver (>= 0.2.0)
     posix-spawn (0.3.11)
     powerpack (0.1.1)
+    premailer (1.8.6)
+      css_parser (>= 1.3.6)
+      htmlentities (>= 4.0.0)
+    premailer-rails (1.9.0)
+      actionmailer (>= 3, < 5)
+      premailer (~> 1.7, >= 1.7.9)
     pry (0.10.3)
       coderay (~> 1.1.0)
       method_source (~> 0.8.1)
@@ -615,7 +622,7 @@ GEM
       activesupport (= 4.2.5.2)
       rake (>= 0.8.7)
       thor (>= 0.18.1, < 2.0)
-    rainbow (2.0.0)
+    rainbow (2.1.0)
     raindrops (0.15.0)
     rake (10.5.0)
     raphael-rails (2.1.2)
@@ -687,13 +694,12 @@ GEM
     rspec-retry (0.4.5)
       rspec-core
     rspec-support (3.3.0)
-    rubocop (0.35.1)
-      astrolabe (~> 1.3)
-      parser (>= 2.2.3.0, < 3.0)
+    rubocop (0.38.0)
+      parser (>= 2.3.0.6, < 3.0)
       powerpack (~> 0.1)
       rainbow (>= 1.99.1, < 3.0)
       ruby-progressbar (~> 1.7)
-      tins (<= 1.6.0)
+      unicode-display_width (~> 1.0, >= 1.0.1)
     ruby-fogbugz (0.2.1)
       crack (~> 0.4)
     ruby-progressbar (1.7.5)
@@ -843,6 +849,7 @@ GEM
     unf (0.1.4)
       unf_ext
     unf_ext (0.0.7.1)
+    unicode-display_width (1.0.2)
     unicorn (4.9.0)
       kgio (~> 2.6)
       rack
@@ -942,7 +949,7 @@ DEPENDENCIES
   github-markup (~> 1.3.1)
   gitlab-flowdock-git-hook (~> 1.0.1)
   gitlab_emoji (~> 0.3.0)
-  gitlab_git (~> 9.0)
+  gitlab_git (~> 10.0)
   gitlab_meta (= 7.0)
   gitlab_omniauth-ldap (~> 1.2.1)
   gollum-lib (~> 4.1.0)
@@ -992,6 +999,7 @@ DEPENDENCIES
   paranoia (~> 2.0)
   pg (~> 0.18.2)
   poltergeist (~> 1.9.0)
+  premailer-rails (~> 1.9.0)
   pry-rails
   quiet_assets (~> 1.0.2)
   rack-attack (~> 4.3.1)
@@ -1013,7 +1021,7 @@ DEPENDENCIES
   rqrcode-rails3 (~> 0.1.7)
   rspec-rails (~> 3.3.0)
   rspec-retry
-  rubocop (~> 0.35.0)
+  rubocop (~> 0.38.0)
   ruby-fogbugz (~> 0.2.1)
   sanitize (~> 2.0)
   sass-rails (~> 5.0.0)
@@ -1056,6 +1064,3 @@ DEPENDENCIES
   web-console (~> 2.0)
   webmock (~> 1.21.0)
   wikicloth (= 0.8.1)
-
-BUNDLED WITH
-   1.11.2
diff --git a/README.md b/README.md
index 208427fcf8c38727985b3e20b24173dfb18eb2a5..afa60116ebba5b1c38e2e4b9174d90d41f6c74e3 100644
--- a/README.md
+++ b/README.md
@@ -68,7 +68,7 @@ GitLab is a Ruby on Rails application that runs on the following software:
 
 - Ubuntu/Debian/CentOS/RHEL
 - Ruby (MRI) 2.1
-- Git 2.7.3+
+- Git 2.7.4+
 - Redis 2.8+
 - MySQL or PostgreSQL
 
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index d415bbd3476009302132314beadf03b0b1220a21..01451830653174d7893e2acccd89713c08f51eb8 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -7,6 +7,7 @@
 #= require jquery
 #= require jquery-ui/autocomplete
 #= require jquery-ui/datepicker
+#= require jquery-ui/draggable
 #= require jquery-ui/effect-highlight
 #= require jquery-ui/sortable
 #= require jquery_ujs
@@ -138,7 +139,7 @@ $ ->
 
   # Initialize tooltips
   $('body').tooltip(
-    selector: '.has_tooltip, [data-toggle="tooltip"]'
+    selector: '.has-tooltip, [data-toggle="tooltip"]'
     placement: (_, el) ->
       $el = $(el)
       $el.data('placement') || 'bottom'
diff --git a/app/assets/javascripts/aside.js.coffee b/app/assets/javascripts/aside.js.coffee
index 854731019443e3ea1d49e73a3fa5a12e5fc00185..66ab505432672d66818ee09899c8ebc51f6f90ed 100644
--- a/app/assets/javascripts/aside.js.coffee
+++ b/app/assets/javascripts/aside.js.coffee
@@ -5,7 +5,6 @@ class @Aside
       e.preventDefault()
       btn = $(e.currentTarget)
       icon = btn.find('i')
-      console.log('1')
 
       if icon.hasClass('fa-angle-left')
         btn.parent().find('section').hide()
diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee
index 03a4487416136a45db0ac73d6e8a2215e859e478..47b080406d4046fd99c9c21fd06dede2aae2a59d 100644
--- a/app/assets/javascripts/awards_handler.coffee
+++ b/app/assets/javascripts/awards_handler.coffee
@@ -122,7 +122,7 @@ class @AwardsHandler
 
     nodes = []
     nodes.push(
-      "<button class='btn award-control js-emoji-btn has_tooltip active' title='me'>",
+      "<button class='btn award-control js-emoji-btn has-tooltip active' title='me'>",
       "<div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>",
       "<span class='award-control-text js-counter'>1</span>",
       "</button>"
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index 1be86e3b8200289323755b2e39c806c0233e00ca..f5e1ca9860da1c6337202cb70ae117bbe584d831 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -14,7 +14,6 @@ class Dispatcher
 
     path = page.split(':')
     shortcut_handler = null
-
     switch page
       when 'projects:issues:index'
         Issues.init()
@@ -25,6 +24,8 @@ class Dispatcher
         new ZenMode()
       when 'projects:milestones:show', 'groups:milestones:show', 'dashboard:milestones:show'
         new Milestone()
+      when 'dashboard:todos:index'
+        new Todos()
       when 'projects:milestones:new', 'projects:milestones:edit'
         new ZenMode()
         new DropzoneInput($('.milestone-form'))
diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee
index 4f03847775592ba453befac00f548e9ee751eeba..960585245d797536149bb4e575903f734fc1bc32 100644
--- a/app/assets/javascripts/gl_dropdown.js.coffee
+++ b/app/assets/javascripts/gl_dropdown.js.coffee
@@ -167,7 +167,11 @@ class GitLabDropdown
 
   hidden: =>
     if @options.filterable
-      @dropdown.find(".dropdown-input-field").blur().val("")
+      @dropdown
+        .find(".dropdown-input-field")
+        .blur()
+        .val("")
+        .trigger("keyup")
 
     if @dropdown.find(".dropdown-toggle-page").length
       $('.dropdown-menu', @dropdown).removeClass PAGE_TWO_CLASS
@@ -246,11 +250,15 @@ class GitLabDropdown
         if oldValue
           value = "#{oldValue},#{value}"
       else
-        @dropdown.find(ACTIVE_CLASS).removeClass ACTIVE_CLASS
+        @dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS
 
       # Toggle active class for the tick mark
       el.toggleClass "is-active"
 
+      # Toggle the dropdown label
+      if @options.toggleLabel
+        $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selectedObject)
+
       if value?
         if !field.length
           # Create hidden input for form
diff --git a/app/assets/javascripts/issuable_form.js.coffee b/app/assets/javascripts/issuable_form.js.coffee
index 48c249943f2d25489d5bfd916a012fa98d496bd1..7a788f761b7ac87493bf5d558297660bc4e77a81 100644
--- a/app/assets/javascripts/issuable_form.js.coffee
+++ b/app/assets/javascripts/issuable_form.js.coffee
@@ -1,4 +1,7 @@
 class @IssuableForm
+  issueMoveConfirmMsg: 'Are you sure you want to move this issue to another project?'
+  wipRegex: /^\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i
+
   constructor: (@form) ->
     GitLab.GfmAutoComplete.setup()
     new UsersSelect()
@@ -6,14 +9,17 @@ class @IssuableForm
 
     @titleField       = @form.find("input[name*='[title]']")
     @descriptionField = @form.find("textarea[name*='[description]']")
+    @issueMoveField   = @form.find("#move_to_project_id")
 
     return unless @titleField.length && @descriptionField.length
 
     @initAutosave()
 
-    @form.on "submit", @resetAutosave
+    @form.on "submit", @handleSubmit
     @form.on "click", ".btn-cancel", @resetAutosave
 
+    @initWip()
+
   initAutosave: ->
     new Autosave @titleField, [
       document.location.pathname,
@@ -27,6 +33,50 @@ class @IssuableForm
       "description"
     ]
 
+  handleSubmit: =>
+    if (parseInt(@issueMoveField?.val()) ? 0) > 0
+      return false unless confirm(@issueMoveConfirmMsg)
+
+    @resetAutosave()
+
   resetAutosave: =>
     @titleField.data("autosave").reset()
     @descriptionField.data("autosave").reset()
+
+  initWip: ->
+    @$wipExplanation = @form.find(".js-wip-explanation")
+    @$noWipExplanation = @form.find(".js-no-wip-explanation")
+    return unless @$wipExplanation.length and @$noWipExplanation.length
+
+    @form.on "click", ".js-toggle-wip", @toggleWip
+
+    @titleField.on "keyup blur", @renderWipExplanation
+
+    @renderWipExplanation()
+
+  workInProgress: ->
+    @wipRegex.test @titleField.val()
+
+  renderWipExplanation: =>
+    if @workInProgress()
+      @$wipExplanation.show()
+      @$noWipExplanation.hide()
+    else
+      @$wipExplanation.hide()
+      @$noWipExplanation.show()
+
+  toggleWip: (event) =>
+    event.preventDefault()
+
+    if @workInProgress()
+      @removeWip()
+    else
+      @addWip()
+
+    @renderWipExplanation()
+
+  removeWip: ->
+    @titleField.val @titleField.val().replace(@wipRegex, "")
+
+  addWip: ->
+    @titleField.val "WIP: #{@titleField.val()}"
diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee
index d663e34871c244bccefb4b01bfdab00b12a206d6..f50df1f5ea3f1da8f8d07baa5bf43a702d8e612e 100644
--- a/app/assets/javascripts/issue.js.coffee
+++ b/app/assets/javascripts/issue.js.coffee
@@ -7,6 +7,7 @@ class @Issue
     # Prevent duplicate event bindings
     @disableTaskList()
     @fixAffixScroll()
+    @initParticipants()
     if $('a.btn-close').length
       @initTaskList()
       @initIssueBtnEventListeners()
@@ -84,3 +85,27 @@ class @Issue
       type: 'PATCH'
       url: $('form.js-issuable-update').attr('action')
       data: patchData
+
+  initParticipants: ->
+    _this = @
+    $(document).on "click", ".js-participants-more", @toggleHiddenParticipants
+
+    $(".js-participants-author").each (i) ->
+      if i >= _this.PARTICIPANTS_ROW_COUNT
+        $(@)
+          .addClass "js-participants-hidden"
+          .hide()
+
+  toggleHiddenParticipants: (e) ->
+    e.preventDefault()
+
+    currentText = $(this).text().trim()
+    lessText = $(this).data("less-text")
+    originalText = $(this).data("original-text")
+
+    if currentText is originalText
+      $(this).text(lessText)
+    else
+      $(this).text(originalText)
+
+    $(".js-participants-hidden").toggle()
diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee
index a0acf3028bfbd933902a28db0c4b3ba958bc3d27..1127b289264979acfea06dde32e51bf65575fb53 100644
--- a/app/assets/javascripts/issues.js.coffee
+++ b/app/assets/javascripts/issues.js.coffee
@@ -41,24 +41,28 @@
     @timer = null
     $("#issue_search").keyup ->
       clearTimeout(@timer)
-      @timer = setTimeout(Issues.filterResults, 500)
+      @timer = setTimeout( ->
+        Issues.filterResults $("#issue_search_form")
+      , 500)
 
-  filterResults: =>
-    form = $("#issue_search_form")
-    search = $("#issue_search").val()
-    $('.issues-holder').css("opacity", '0.5')
-    issues_url = form.attr('action') + '?' + form.serialize()
+  filterResults: (form) =>
+    $('.issues-holder, .merge-requests-holder').css("opacity", '0.5')
+    formAction = form.attr('action')
+    formData = form.serialize()
+    issuesUrl = formAction
+    issuesUrl += ("#{if formAction.indexOf("?") < 0 then '?' else '&'}")
+    issuesUrl += formData
 
     $.ajax
       type: "GET"
-      url: form.attr('action')
-      data: form.serialize()
+      url: formAction
+      data: formData
       complete: ->
-        $('.issues-holder').css("opacity", '1.0')
+        $('.issues-holder, .merge-requests-holder').css("opacity", '1.0')
       success: (data) ->
-        $('.issues-holder').html(data.html)
+        $('.issues-holder, .merge-requests-holder').html(data.html)
         # Change url so if user reload a page - search results are saved
-        history.replaceState {page: issues_url}, document.title, issues_url
+        history.replaceState {page: issuesUrl}, document.title, issuesUrl
         Issues.reload()
       dataType: "json"
 
diff --git a/app/assets/javascripts/labels_select.js.coffee b/app/assets/javascripts/labels_select.js.coffee
index 5ade2cb66cb5cb17cb69488afe60e61c1f6c613c..4a0c18a99a6bce7a24791f8fd451abe85f9fe510 100644
--- a/app/assets/javascripts/labels_select.js.coffee
+++ b/app/assets/javascripts/labels_select.js.coffee
@@ -1,30 +1,32 @@
 class @LabelsSelect
   constructor: ->
     $('.js-label-select').each (i, dropdown) ->
-      projectId = $(dropdown).data('project-id')
-      labelUrl = $(dropdown).data("labels")
-      selectedLabel = $(dropdown).data('selected')
+      $dropdown = $(dropdown)
+      projectId = $dropdown.data('project-id')
+      labelUrl = $dropdown.data('labels')
+      selectedLabel = $dropdown.data('selected')
       if selectedLabel
-        selectedLabel = selectedLabel.split(",")
+        selectedLabel = selectedLabel.split(',')
       newLabelField = $('#new_label_name')
       newColorField = $('#new_label_color')
-      showNo = $(dropdown).data('show-no')
-      showAny = $(dropdown).data('show-any')
+      showNo = $dropdown.data('show-no')
+      showAny = $dropdown.data('show-any')
+      defaultLabel = $dropdown.data('default-label')
 
       if newLabelField.length
-        $('.suggest-colors-dropdown a').on "click", (e) ->
+        $('.suggest-colors-dropdown a').on 'click', (e) ->
           e.preventDefault()
           e.stopPropagation()
-          newColorField.val $(this).data("color")
+          newColorField.val $(this).data('color')
           $('.js-dropdown-label-color-preview')
-            .css 'background-color', $(this).data("color")
+            .css 'background-color', $(this).data('color')
             .addClass 'is-active'
 
-        $('.js-new-label-btn').on "click", (e) ->
+        $('.js-new-label-btn').on 'click', (e) ->
           e.preventDefault()
           e.stopPropagation()
 
-          if newLabelField.val() isnt "" && newColorField.val() isnt ""
+          if newLabelField.val() isnt '' and newColorField.val() isnt ''
             $('.js-new-label-btn').disable()
 
             # Create new label with API
@@ -33,46 +35,38 @@ class @LabelsSelect
               color: newColorField.val()
             }, (label) ->
               $('.js-new-label-btn').enable()
-              $('.dropdown-menu-back', $(dropdown).parent()).trigger "click"
+              $('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
 
-      $(dropdown).glDropdown(
+      $dropdown.glDropdown(
         data: (term, callback) ->
-          # We have to fetch the JS version of the labels list because there is no
-          # public facing JSON url for labels
           $.ajax(
             url: labelUrl
           ).done (data) ->
-            html = $(data)
-            data = []
-            html.find('.label-row a').each ->
-              data.push(
-                title: $(@).text().trim()
-              )
-
             if showNo
               data.unshift(
-                id: "0"
-                title: 'No label'
+                id: 0
+                title: 'No Label'
               )
 
             if showAny
               data.unshift(
-                title: 'Any label'
+                isAny: true
+                title: 'Any Label'
               )
 
             if data.length > 2
-              data.splice 2, 0, "divider"
+              data.splice 2, 0, 'divider'
 
             callback data
         renderRow: (label) ->
           if $.isArray(selectedLabel)
-            selected = ""
+            selected = ''
             $.each selectedLabel, (i, selectedLbl) ->
               selectedLbl = selectedLbl.trim()
-              if selected is "" && label.title is selectedLbl
-                selected = "is-active"
+              if selected is '' and label.title is selectedLbl
+                selected = 'is-active'
           else
-            selected = if label.title is selectedLabel then "is-active" else ""
+            selected = if label.title is selectedLabel then 'is-active' else ''
 
           "<li>
             <a href='#' class='#{selected}'>
@@ -83,10 +77,24 @@ class @LabelsSelect
         search:
           fields: ['title']
         selectable: true
-        fieldName: $(dropdown).data('field-name')
+        toggleLabel: (selected) ->
+          if selected and selected.title isnt 'Any Label'
+            selected.title
+          else
+            defaultLabel
+        fieldName: $dropdown.data('field-name')
         id: (label) ->
-          label.title
+          if label.isAny?
+            ''
+          else
+            label.title
         clicked: ->
-          if $(dropdown).hasClass "js-filter-submit"
-            $(dropdown).parents('form').submit()
+          page = $('body').data 'page'
+          isIssueIndex = page is 'projects:issues:index'
+          isMRIndex = page is page is 'projects:merge_requests:index'
+
+          if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
+            Issues.filterResults $dropdown.closest('form')
+          else if $dropdown.hasClass 'js-filter-submit'
+            $dropdown.closest('form').submit()
       )
diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee
index 8322b4c46ad0c90126df97c5fc2f62aac4cf928a..839e6ec2c0878934ffae2e983580a52703f433e8 100644
--- a/app/assets/javascripts/merge_request_tabs.js.coffee
+++ b/app/assets/javascripts/merge_request_tabs.js.coffee
@@ -3,6 +3,8 @@
 # Handles persisting and restoring the current tab selection and lazily-loading
 # content on the MergeRequests#show page.
 #
+#= require jquery.cookie
+#
 # ### Example Markup
 #
 #   <ul class="nav-links merge-request-tabs">
@@ -68,11 +70,15 @@ class @MergeRequestTabs
 
     if action == 'commits'
       @loadCommits($target.attr('href'))
+      @expandView()
     else if action == 'diffs'
       @loadDiff($target.attr('href'))
       @shrinkView()
     else if action == 'builds'
       @loadBuilds($target.attr('href'))
+      @expandView()
+    else
+      @expandView()
 
     @setCurrentAction(action)
 
@@ -189,11 +195,24 @@ class @MergeRequestTabs
     $('.container-fluid').removeClass('container-limited')
 
   shrinkView: ->
-    $gutterIcon = $('.js-sidebar-toggle i')
+    $gutterIcon = $('.js-sidebar-toggle i:visible')
 
     # Wait until listeners are set
     setTimeout( ->
-      # Only when sidebar is collapsed
+      # Only when sidebar is expanded
       if $gutterIcon.is('.fa-angle-double-right')
-        $gutterIcon.closest('a').trigger('click',[true])
+        $gutterIcon.closest('a').trigger('click', [true])
+    , 0)
+
+  # Expand the issuable sidebar unless the user explicitly collapsed it
+  expandView: ->
+    return if $.cookie('collapsed_gutter') == 'true'
+
+    $gutterIcon = $('.js-sidebar-toggle i:visible')
+
+    # Wait until listeners are set
+    setTimeout( ->
+      # Only when sidebar is collapsed
+      if $gutterIcon.is('.fa-angle-double-left')
+        $gutterIcon.closest('a').trigger('click', [true])
     , 0)
diff --git a/app/assets/javascripts/milestone_select.js.coffee b/app/assets/javascripts/milestone_select.js.coffee
index 5e884454a6585e9963263dcda575f83ef7fccc34..e17a1adb6484b4d957e7c5a3e088abc3c2a7a69a 100644
--- a/app/assets/javascripts/milestone_select.js.coffee
+++ b/app/assets/javascripts/milestone_select.js.coffee
@@ -1,60 +1,65 @@
 class @MilestoneSelect
   constructor: ->
     $('.js-milestone-select').each (i, dropdown) ->
-      projectId = $(dropdown).data('project-id')
-      milestonesUrl = $(dropdown).data('milestones')
-      selectedMilestone = $(dropdown).data('selected')
-      showNo = $(dropdown).data('show-no')
-      showAny = $(dropdown).data('show-any')
-      useId = $(dropdown).data('use-id')
+      $dropdown = $(dropdown)
+      projectId = $dropdown.data('project-id')
+      milestonesUrl = $dropdown.data('milestones')
+      selectedMilestone = $dropdown.data('selected')
+      showNo = $dropdown.data('show-no')
+      showAny = $dropdown.data('show-any')
+      useId = $dropdown.data('use-id')
+      defaultLabel = $dropdown.data('default-label')
 
-      $(dropdown).glDropdown(
+      $dropdown.glDropdown(
         data: (term, callback) ->
           $.ajax(
             url: milestonesUrl
           ).done (data) ->
-            html = $(data)
-            data = []
-            html.find('.milestone strong a').each ->
-              link = $(@).attr("href").split("/")
-              data.push(
-                id: link[link.length - 1]
-                title: $(@).text().trim()
-              )
-
             if showNo
               data.unshift(
-                id: "0"
+                id: '0'
                 title: 'No Milestone'
               )
 
             if showAny
               data.unshift(
+                isAny: true
                 title: 'Any Milestone'
               )
 
             if data.length > 2
-              data.splice 2, 0, "divider"
+              data.splice 2, 0, 'divider'
 
             callback(data)
         filterable: true
         search:
           fields: ['title']
         selectable: true
-        fieldName: $(dropdown).data('field-name')
+        toggleLabel: (selected) ->
+          if selected && 'id' of selected
+            selected.title
+          else
+            defaultLabel
+        fieldName: $dropdown.data('field-name')
         text: (milestone) ->
           milestone.title
         id: (milestone) ->
           if !useId
-            if milestone.title isnt "Any milestone"
+            if !milestone.isAny?
               milestone.title
             else
-              ""
+              ''
           else
             milestone.id
         isSelected: (milestone) ->
           milestone.title is selectedMilestone
         clicked: ->
-          if $(dropdown).hasClass "js-filter-submit"
-            $(dropdown).parents('form').submit()
+          page = $('body').data 'page'
+          isIssueIndex = page is 'projects:issues:index'
+          isMRIndex = page is page is 'projects:merge_requests:index'
+
+          if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
+            Issues.filterResults $dropdown.closest('form')
+          else if $dropdown.hasClass 'js-filter-submit'
+            $dropdown.closest('form').submit()
       )
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index 75d7f52bbb6e7e7e29ba6129a7ac7238be218ddc..ff06c57f2b5267dc716857b18f2fe80b5553c8ea 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -343,6 +343,7 @@ class @Notes
   updateNote: (_xhr, note, _status) =>
     # Convert returned HTML to a jQuery object so we can modify it further
     $html = $(note.html)
+    $('.js-timeago', $html).timeago()
     $html.syntaxHighlight()
     $html.find('.js-task-list-container').taskList('enable')
 
@@ -360,14 +361,12 @@ class @Notes
   showEditForm: (e) ->
     e.preventDefault()
     note = $(this).closest(".note")
-    note.find(".note-body > .note-text").hide()
-    note.find(".note-header").hide()
+    note.addClass "is-editting"
     form = note.find(".note-edit-form")
     isNewForm = form.is(':not(.gfm-form)')
     if isNewForm
       form.addClass('gfm-form')
     form.addClass('current-note-edit-form')
-    form.show()
 
     # Show the attachment delete link
     note.find(".js-note-attachment-delete").show()
@@ -401,11 +400,9 @@ class @Notes
   cancelEdit: (e) ->
     e.preventDefault()
     note = $(this).closest(".note")
-    note.find(".note-body > .note-text").show()
-    note.find(".note-header").show()
+    note.removeClass "is-editting"
     note.find(".current-note-edit-form")
       .removeClass("current-note-edit-form")
-      .hide()
 
   ###
   Called in response to deleting a note of any kind.
@@ -626,10 +623,10 @@ class @Notes
       if closebtn.text() isnt closetext
         closebtn.text(closetext)
 
-      if reopenbtn.is(':not(.btn-comment-and-reopen)')
+      if reopenbtn.is('.btn-comment-and-reopen')
         reopenbtn.removeClass('btn-comment-and-reopen')
 
-      if closebtn.is(':not(.btn-comment-and-close)')
+      if closebtn.is('.btn-comment-and-close')
         closebtn.removeClass('btn-comment-and-close')
 
       if discardbtn.is(':visible')
diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee
index 76bc4ff42a2ea0199d281dd002b2b0676443281e..87d313ed67c271e7549758c765535fc0ea607b18 100644
--- a/app/assets/javascripts/project.js.coffee
+++ b/app/assets/javascripts/project.js.coffee
@@ -11,7 +11,6 @@ class @Project
       $(@).toggleClass('active')
 
       url = $("#project_clone").val()
-      console.log("url",url)
 
       # Update the input field
       $('#project_clone').val(url)
diff --git a/app/assets/javascripts/project_new.js.coffee b/app/assets/javascripts/project_new.js.coffee
index fecdb9fc2e773436fe8ac193903d8d97ed2cf533..63dee4ed5d79ea823e036070c58b1a9af680bf16 100644
--- a/app/assets/javascripts/project_new.js.coffee
+++ b/app/assets/javascripts/project_new.js.coffee
@@ -3,3 +3,16 @@ class @ProjectNew
     $('.project-edit-container').on 'ajax:before', =>
       $('.project-edit-container').hide()
       $('.save-project-loader').show()
+    @toggleSettings()
+    @toggleSettingsOnclick()
+
+
+  toggleSettings: ->
+    checked = $("#project_builds_enabled").prop("checked")
+    if checked
+      $('.builds-feature').show()
+    else
+      $('.builds-feature').hide()
+
+  toggleSettingsOnclick: ->
+    $("#project_builds_enabled").on 'click', @toggleSettings
diff --git a/app/assets/javascripts/stat_graph_contributors_util.js.coffee b/app/assets/javascripts/stat_graph_contributors_util.js.coffee
index f5584bcfe4b28c5fbd80486bc56168e86fc49a47..31617c88b4a63bbb969553c68541704963e61000 100644
--- a/app/assets/javascripts/stat_graph_contributors_util.js.coffee
+++ b/app/assets/javascripts/stat_graph_contributors_util.js.coffee
@@ -95,4 +95,4 @@ window.ContributorsStatGraphUtil =
     if date_range is null || date_range[0] <= new Date(date) <= date_range[1]
       true
     else
-      false
\ No newline at end of file
+      false
diff --git a/app/assets/javascripts/todos.js.coffee b/app/assets/javascripts/todos.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..b6b4bd90e6afa2f394e6d0b2123e36d36167341d
--- /dev/null
+++ b/app/assets/javascripts/todos.js.coffee
@@ -0,0 +1,56 @@
+class @Todos
+  constructor: (@name) ->
+    @clearListeners()
+    @initBtnListeners()
+
+  clearListeners: ->
+    $('.done-todo').off('click')
+    $('.js-todos-mark-all').off('click')
+
+  initBtnListeners: ->
+    $('.done-todo').on('click', @doneClicked)
+    $('.js-todos-mark-all').on('click', @allDoneClicked)
+
+  doneClicked: (e) =>
+    e.preventDefault()
+    e.stopImmediatePropagation()
+
+    $this = $(e.currentTarget)
+    $this.disable()
+
+    $.ajax
+      type: 'POST'
+      url: $this.attr('href')
+      dataType: 'json'
+      data: '_method': 'delete'
+      success: (data) =>
+        @clearDone $this.closest('li')
+        @updateBadges data
+
+  allDoneClicked: (e) =>
+    e.preventDefault()
+    e.stopImmediatePropagation()
+
+    $this = $(e.currentTarget)
+    $this.disable()
+
+    $.ajax
+      type: 'POST'
+      url: $this.attr('href')
+      dataType: 'json'
+      data: '_method': 'delete'
+      success: (data) =>
+        $this.remove()
+        $('.js-todos-list').remove()
+        @updateBadges data
+
+  clearDone: ($row) ->
+    $ul = $row.closest('ul')
+    $row.remove()
+
+    if not $ul.find('li').length
+      $ul.parents('.panel').remove()
+
+  updateBadges: (data) ->
+    $('.todos-pending .badge, .todos-pending-count').text data.count
+    $('.todos-done .badge').text data.done_count
diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee
index 987c6f4b8d2123bafb1d156a11350b5632648d4d..3d6452d2f4693d36691df7ff987bc257de83d761 100644
--- a/app/assets/javascripts/users_select.js.coffee
+++ b/app/assets/javascripts/users_select.js.coffee
@@ -4,14 +4,16 @@ class @UsersSelect
     @userPath = "/autocomplete/users/:id.json"
 
     $('.js-user-search').each (i, dropdown) =>
-      @projectId = $(dropdown).data('project-id')
-      @showCurrentUser = $(dropdown).data('current-user')
-      showNullUser = $(dropdown).data('null-user')
-      showAnyUser = $(dropdown).data('any-user')
-      firstUser = $(dropdown).data('first-user')
-      selectedId = $(dropdown).data('selected')
-
-      $(dropdown).glDropdown(
+      $dropdown = $(dropdown)
+      @projectId = $dropdown.data('project-id')
+      @showCurrentUser = $dropdown.data('current-user')
+      showNullUser = $dropdown.data('null-user')
+      showAnyUser = $dropdown.data('any-user')
+      firstUser = $dropdown.data('first-user')
+      selectedId = $dropdown.data('selected')
+      defaultLabel = $dropdown.data('default-label')
+
+      $dropdown.glDropdown(
         data: (term, callback) =>
           @users term, (users) =>
             if term.length is 0
@@ -52,10 +54,21 @@ class @UsersSelect
         search:
           fields: ['name', 'username']
         selectable: true
-        fieldName: $(dropdown).data('field-name')
+        fieldName: $dropdown.data('field-name')
+        toggleLabel: (selected) ->
+          if selected && 'id' of selected
+            selected.name
+          else
+            defaultLabel
         clicked: ->
-          if $(dropdown).hasClass "js-filter-submit"
-            $(dropdown).parents('form').submit()
+          page = $('body').data 'page'
+          isIssueIndex = page is 'projects:issues:index'
+          isMRIndex = page is page is 'projects:merge_requests:index'
+
+          if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
+            Issues.filterResults $dropdown.closest('form')
+          else if $dropdown.hasClass 'js-filter-submit'
+            $dropdown.closest('form').submit()
         renderRow: (user) ->
           username = if user.username then "@#{user.username}" else ""
           avatar = if user.avatar_url then user.avatar_url else false
diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index b7ffa3e6ffbb6f6b7132b11f17d151a80cf1359e..5aa425dab6c9214e0d11254c8014c29e38fd6ee0 100644
--- a/app/assets/stylesheets/framework/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
@@ -16,7 +16,7 @@
   }
 
   &.group-avatar, &.project-avatar, &.avatar-tile {
-    @include border-radius(0px);
+    @include border-radius(0);
   }
 
   &.s16 { width: 16px; height: 16px; margin-right: 6px; }
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 90c3ce0e84cc47990cace860da38aeece6cefb44..62b2af0dbf7ee1106c565b97309e25a49f41dcb0 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -28,10 +28,6 @@
   border-bottom: 1px solid $border-color;
   color: $gl-gray;
 
-  a {
-    color: $md-link-color;
-  }
-
   &.oneline-block {
     line-height: 42px;
   }
@@ -111,10 +107,28 @@
     margin: 0;
     font-size: 23px;
     font-weight: normal;
-    margin: 16px 0 5px 0;
+    margin: 16px 0 5px;
     color: #4c4e54;
     font-size: 23px;
     line-height: 1.1;
+
+    h1 {
+      color: #313236;
+      margin-bottom: 6px;
+      font-size: 23px;
+    }
+
+    .visibility-icon {
+      display: inline-block;
+      margin-left: 5px;
+      font-size: 18px;
+      color: $gray;
+    }
+
+    p {
+      padding: 0 $gl-padding;
+      color: #5c5d5e;
+    }
   }
 
   .cover-desc {
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index fa115a4bf567118c17acd9442a7523cf8baac942..657c5f033c700335f74dd186f247c3f04f78645c 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -208,3 +208,13 @@
     background-color: #e4e7ed !important;
   }
 }
+
+.btn-loading {
+  &:not(.disabled) .fa {
+    display: none;
+  }
+
+  .fa {
+    margin-right: 5px;
+  }
+}
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 3197ea84460fb7f43e690bbef996eec5f4194936..d92cf6e6c44f7ca4c1eeaec30e4c01e632a20c38 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -9,6 +9,12 @@
   border-left: $caret-width-base solid transparent;
 }
 
+.btn-group {
+  .caret {
+    margin-left: 0;
+  }
+}
+
 .dropdown {
   position: relative;
 }
@@ -69,7 +75,7 @@
   width: 240px;
   margin-top: 2px;
   margin-bottom: 0;
-  padding: 10px 10px;
+  padding: 10px;
   font-size: 14px;
   font-weight: normal;
   background-color: $dropdown-bg;
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index c431e2b0df3de88990a14bbef455ab6972b8a3e1..40a508c1ebc73fa96137f1e510e8f6026fc97740 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -3,22 +3,11 @@
   vertical-align: top;
 }
 
-@media (min-width: 800px)  {
+@media (min-width: $screen-sm-min)  {
   .issues-filters,
   .issues_bulk_update {
-    select, .select2-container {
-      width: 120px !important;
-      display: inline-block;
-    }
-  }
-}
-
-@media (min-width: 1200px) {
-  .issues-filters,
-  .issues_bulk_update {
-    select, .select2-container {
-      width: 150px !important;
-      display: inline-block;
+    .dropdown-menu-toggle {
+      width: 132px;
     }
   }
 }
diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss
index 2a4cf4fc335ee49ee3adadfe584ec160f1633ae7..c83cf881596f646665b2cf7830a87871cb7f1513 100644
--- a/app/assets/stylesheets/framework/gitlab-theme.scss
+++ b/app/assets/stylesheets/framework/gitlab-theme.scss
@@ -117,4 +117,4 @@ body {
   &.ui_violet {
     @include gitlab-theme(#98c, $theme-violet, #436, #325);
   }
-}
\ No newline at end of file
+}
diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss
index e901c78d02fb06ee9b87b78771be024bc66a2b87..8bb047db2ddbc4543a90b2a99ec2385720cf458f 100644
--- a/app/assets/stylesheets/framework/layout.scss
+++ b/app/assets/stylesheets/framework/layout.scss
@@ -16,7 +16,7 @@ body {
 }
 
 .container .content {
-  margin: 0 0;
+  margin: 0;
 }
 
 .navless-container {
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index bfec0911b3ca925ba8ba9c13a3271fc029ffdae4..b17c8bcbb1eb3e33a38067cdbbb36cf2d47d3093 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -111,14 +111,17 @@ ul.content-list {
 
   > li {
     border-color: $table-border-color;
-    color: $list-text-color;
     font-size: $list-font-size;
+    color: $list-text-color;
 
     .title {
-      color: $list-title-color;
       font-weight: 600;
     }
 
+    a {
+      color: $gl-dark-link-color;
+    }
+
     .description {
       p {
         @include str-truncated;
@@ -141,6 +144,10 @@ ul.content-list {
   }
 }
 
+.panel > .content-list > li {
+  padding: $gl-padding-top $gl-padding;
+}
+
 ul.controls {
   padding-top: 1px;
   float: right;
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index 377bfa174bd44bddbce7b8619d97184ff3528e59..250d630929176d2a9f35b4c545561bc3d66121f1 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -1,7 +1,7 @@
 /**
  * Generic mixins
  */
- @mixin box-shadow($shadow) {
+@mixin box-shadow($shadow) {
   -webkit-box-shadow: $shadow;
   -moz-box-shadow: $shadow;
   -ms-box-shadow: $shadow;
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index b3371229d5a55cd0d3c24484526b67b99376e1e0..fa7944cdabe905eeccf43c2556724bff1537d163 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -41,7 +41,7 @@
 }
 
 .select2-drop {
-  @include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px);
+  @include box-shadow(rgba(76, 86, 103, 0.247059) 0 0 1px 0, rgba(31, 37, 50, 0.317647) 0 2px 18px 0);
   @include border-radius ($border-radius-default);
   border: none;
 }
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 949295a1d0cf0984dfb6016a78da5cda47220a34..b1886fbe67b64ac4295ac3b52aeedd343924d126 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -39,8 +39,8 @@
   h1 {
     font-size: 1.3em;
     font-weight: 600;
-    margin: 24px 0 12px 0;
-    padding: 0 0 10px 0;
+    margin: 24px 0 12px;
+    padding: 0 0 10px;
     border-bottom: 1px solid #e7e9ed;
     color: #313236;
   }
@@ -48,27 +48,27 @@
   h2 {
     font-size: 1.2em;
     font-weight: 600;
-    margin: 24px 0 12px 0;
+    margin: 24px 0 12px;
     color: #313236;
   }
 
   h3 {
-    margin: 24px 0 12px 0;
+    margin: 24px 0 12px;
     font-size: 1.1em;
   }
 
   h4 {
-    margin: 24px 0 12px 0;
+    margin: 24px 0 12px;
     font-size: 0.98em;
   }
 
   h5 {
-    margin: 24px 0 12px 0;
+    margin: 24px 0 12px;
     font-size: 0.95em;
   }
 
   h6 {
-    margin: 24px 0 12px 0;
+    margin: 24px 0 12px;
     font-size: 0.90em;
   }
 
@@ -76,7 +76,7 @@
     color: #7f8fa4;
     font-size: inherit;
     padding: 8px 21px;
-    margin: 12px 0 12px;
+    margin: 12px 0;
     border-left: 3px solid #e7e9ed;
   }
 
@@ -88,13 +88,13 @@
 
   p {
     color: #5c5d5e;
-    margin: 6px 0 0 0;
+    margin: 6px 0 0;
   }
 
   table {
     @extend .table;
     @extend .table-bordered;
-    margin: 12px 0 12px 0;
+    margin: 12px 0;
     color: #5c5d5e;
     th {
       background: #f8fafc;
@@ -102,7 +102,7 @@
   }
 
   pre {
-    margin: 12px 0 12px 0;
+    margin: 12px 0;
     font-size: 13px;
     line-height: 1.6em;
     overflow-x: auto;
@@ -191,7 +191,7 @@ body {
   line-height: 1.3;
   font-size: 1.25em;
   font-weight: 600;
-  margin: 12px 7px 12px 7px;
+  margin: 12px 7px;
 }
 
 h1, h2, h3, h4, h5, h6 {
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 211ead7319d38793a8b271ec951474d39362767f..be626678bd7a88e3682007c8fda9e66b9848c5a9 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -1,45 +1,75 @@
-$row-hover: #f4f8fe;
-$gl-text-color: #54565b;
-$gl-text-green: #4a2;
-$gl-text-red: #d12f19;
-$gl-text-orange: #d90;
-$gl-header-color: #323232;
-$gl-link-color: #333c48;
-$md-text-color: #444;
-$md-link-color: #3084bb;
-$progress-color: #c0392b;
-$gl-font-size: 15px;
-$list-font-size: 15px;
+/*
+ * Layout
+ */
 $sidebar_collapsed_width: 62px;
 $sidebar_width: 230px;
 $gutter_collapsed_width: 62px;
 $gutter_width: 290px;
 $gutter_inner_width: 258px;
-$avatar_radius: 50%;
+
+/*
+ * UI elements
+ */
+$border-color:       #efeff1;
+$table-border-color: #eef0f2;
+$background-color:   #faf9f9;
+
+/*
+ * Text
+ */
+$gl-font-size:         15px;
+$gl-title-color:       #333;
+$gl-text-color:        #555;
+$gl-text-green:        #4a2;
+$gl-text-red:          #d12f19;
+$gl-text-orange:       #d90;
+$gl-link-color:        #3084bb;
+$gl-dark-link-color:   #333;
+$gl-placeholder-color: #8f8f8f;
+$gl-gray:              $gl-text-color;
+$gl-header-color:      $gl-title-color;
+
+/*
+ * Lists
+ */
+$list-font-size:   $gl-font-size;
+$list-title-color: $gl-title-color;
+$list-text-color:  $gl-text-color;
+
+/*
+ * Markdown
+ */
+$md-text-color: $gl-text-color;
+$md-link-color: $gl-link-color;
+
+/*
+ * Code
+ */
 $code_font_size: 13px;
 $code_line_height: 1.5;
-$border-color: #efeff1;
-$table-border-color: #eef0f2;
-$background-color: #faf9f9;
-$header-height: 58px;
-$fixed-layout-width: 1280px;
-$gl-gray: #5a5a5a;
+
+/*
+ * Padding
+ */
 $gl-padding: 16px;
 $gl-btn-padding: 10px;
 $gl-vert-padding: 6px;
 $gl-padding-top: 10px;
+
+/*
+ * Misc
+ */
+$row-hover: #f4f8fe;
+$progress-color: #c0392b;
+$avatar_radius: 50%;
+$header-height: 58px;
+$fixed-layout-width: 1280px;
 $gl-avatar-size: 40px;
-$secondary-text: #7f8fa4;
 $error-exclamation-point: #e62958;
 $border-radius-default: 3px;
-$list-title-color: #333;
-$list-text-color: #555;
-
 $btn-transparent-color: #8f8f8f;
-
 $ssh-key-icon-color: #8f8f8f;
 $ssh-key-icon-size: 18px;
-
 $provider-btn-group-border: #e5e5e5;
 $provider-btn-not-active-color: #4688f1;
 
diff --git a/app/assets/stylesheets/notify.scss b/app/assets/stylesheets/notify.scss
new file mode 100644
index 0000000000000000000000000000000000000000..f1d42f80f56357dd708a3e173c5f0b4b827bc48e
--- /dev/null
+++ b/app/assets/stylesheets/notify.scss
@@ -0,0 +1,24 @@
+img {
+  max-width: 100%;
+  height: auto;
+}
+p.details {
+  font-style:italic;
+  color:#777
+}
+.footer p {
+  font-size:small;
+  color:#777
+}
+pre.commit-message {
+  white-space: pre-wrap;
+}
+.file-stats a {
+  text-decoration: none;
+}
+.file-stats .new-file {
+  color: #090;
+}
+.file-stats .deleted-file {
+  color: #B00;
+}
diff --git a/app/assets/stylesheets/pages/admin.scss b/app/assets/stylesheets/pages/admin.scss
index a61161810a3cbd69baf9fcaa44f29f33b84df0c0..e05f14e7496e1537b0c9da3d61995e8fbdffd893 100644
--- a/app/assets/stylesheets/pages/admin.scss
+++ b/app/assets/stylesheets/pages/admin.scss
@@ -34,9 +34,9 @@
     background: #fff
    }
 
-   .visibility-levels {
-     .controls {
-       margin-bottom: 9px;
+  .visibility-levels {
+    .controls {
+      margin-bottom: 9px;
     }
 
     i {
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index d57be1b2daa97fdbef1f4c8b75f194c8d1867dce..33b3c7558ed79598a5250e3dbfcf5594e394df24 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -55,7 +55,7 @@ li.commit {
     }
 
     .commit-row-message {
-      color: $gl-link-color;
+      color: $gl-dark-link-color;
 
       &:hover {
         text-decoration: underline;
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index db06b8288c28323d558bd22d5b7b6313df95ecab..f1368d74b3b9e9a1039be2b1de12be94bcaf78d4 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -1,7 +1,7 @@
 // Common
 .diff-file {
   border: 1px solid $border-color;
-  border-top: none;
+  margin-bottom: $gl-padding;
 
   .diff-header {
     position: relative;
@@ -132,7 +132,7 @@
     }
     .image-info {
       font-size: 12px;
-      margin: 5px 0 0 0;
+      margin: 5px 0 0;
       color: grey;
     }
 
@@ -361,3 +361,11 @@
     border-color: $border;
   }
 }
+
+.files {
+  margin-top: -1px;
+
+  .diff-file:last-child {
+    margin-bottom: 0;
+  }
+}
diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss
index b39a9abf40f3da05cc4ae12c70e436e6813c606e..84eefd01cfe6480cf294f0e82e154e2d55d82f2c 100644
--- a/app/assets/stylesheets/pages/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -6,7 +6,7 @@
   font-size: $gl-font-size;
   padding: $gl-padding-top 0 $gl-padding-top ($gl-avatar-size + $gl-padding-top);
   border-bottom: 1px solid $table-border-color;
-  color: #7f8fa4;
+  color: $list-text-color;
 
   &.event-inline {
     .avatar {
@@ -21,7 +21,7 @@
   }
 
   a {
-    color: #4c4e54;
+    color: $gl-dark-link-color;
   }
 
   .avatar {
@@ -31,10 +31,7 @@
   .event-title {
     @include str-truncated(calc(100% - 174px));
     font-weight: 600;
-
-    .author_name {
-      color: #333;
-    }
+    color: $list-text-color;
   }
 
   .event-body {
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index c975ca0ce434d3768c72e6de44365aabecba71fd..5300bb52a1b543a9335e1349b062aba961183d2c 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -1,34 +1,3 @@
-@media (max-width: $screen-sm-max) {
-  .issuable-affix {
-    margin-top: 20px;
-  }
-}
-
-@media (max-width: $screen-md-max) {
-  .issuable-affix {
-    position: static;
-  }
-}
-
-@media (min-width: $screen-md-max) {
-  .issuable-affix {
-    &.affix-top {
-      position: static;
-    }
-
-    &.affix {
-      position: fixed;
-      top: 70px;
-      margin-right: 35px;
-
-      &.no-affix {
-        position: relative;
-        top: 0;
-      }
-    }
-  }
-}
-
 .issuable-details {
   section {
     .issuable-discussion {
@@ -54,6 +23,10 @@
       padding: 6px 10px;
     }
   }
+
+  &.has-labels {
+    margin-bottom: -5px;
+  }
 }
 
 .issuable-sidebar {
@@ -66,8 +39,9 @@
     width: $gutter_inner_width;
     // --
 
-    &:first-child {
-      padding-top: 5px;
+    &.issuable-sidebar-header {
+      padding-top: 0;
+      padding-bottom: 10px;
     }
 
     &:last-child {
@@ -75,7 +49,6 @@
     }
 
     span {
-      margin-top: 7px;
       display: inline-block;
     }
 
@@ -84,7 +57,7 @@
     }
 
     .issuable-count {
-
+      margin-top: 7px;
     }
 
     .gutter-toggle {
@@ -99,19 +72,19 @@
 
   .title {
     color: $gl-text-color;
-    margin-bottom: 8px;
+    margin-bottom: 10px;
+    line-height: 1;
 
     .avatar {
       margin-left: 0;
     }
 
-    label {
-      font-weight: normal;
-      margin-right: 4px;
-    }
-
     .edit-link {
       color: $gl-gray;
+
+      &:hover {
+        color: $md-link-color;
+      }
     }
   }
 
@@ -144,11 +117,6 @@
   .btn-clipboard {
     color: $gl-gray;
   }
-
-  .participants .avatar {
-    margin-top: 6px;
-    margin-right: 2px;
-  }
 }
 
 .right-sidebar {
@@ -163,8 +131,12 @@
   &.right-sidebar-expanded {
     width: $gutter_width;
 
-    hr {
-      display: none;
+    .value {
+      line-height: 1;
+    }
+
+    .bold {
+      font-weight: 600;
     }
 
     .sidebar-collapsed-icon {
@@ -172,8 +144,23 @@
     }
 
     .gutter-toggle {
+      margin-top: 7px;
       border-left: 1px solid $border-gray-light;
     }
+
+    .assignee .avatar {
+      float: left;
+      margin-right: 10px;
+      margin-bottom: 0;
+      margin-left: 0;
+    }
+
+    .username {
+      display: block;
+      margin-top: 4px;
+      font-size: 13px;
+      font-weight: normal;
+    }
   }
 
   .subscribe-button {
@@ -193,28 +180,26 @@
     width: $sidebar_collapsed_width;
     padding-top: 0;
 
-    hr {
-      margin: 0;
-      color: $gray-normal;
-      border-color: $gray-normal;
-      width: 62px;
-      margin-left: -20px
-    }
-
     .block {
       width: $sidebar_collapsed_width - 1px;
       margin-left: -19px;
-      padding: 15px 0 0 0;
+      padding: 15px 0 0;
       border-bottom: none;
       overflow: hidden;
     }
 
+    .participants {
+      border-bottom: 1px solid $border-gray-light;
+    }
+
     .hide-collapsed {
       display: none;
     }
 
     .gutter-toggle {
-      margin-left: -36px;
+      width: 100%;
+      margin-left: 0;
+      padding-left: 25px;
     }
 
     .sidebar-collapsed-icon {
@@ -229,6 +214,10 @@
         margin-top: 0;
       }
 
+      .author {
+        display: none;
+      }
+
       .btn-clipboard {
         border: none;
 
@@ -241,6 +230,11 @@
         }
       }
     }
+
+    .sidebar-collapsed-user {
+      padding-bottom: 0;
+      margin-bottom: 10px;
+    }
   }
 
   .btn {
@@ -251,6 +245,13 @@
       border: 1px solid $border-gray-dark;
     }
   }
+
+  a:not(.btn) {
+    &:hover {
+      color: $md-link-color;
+      text-decoration: none;
+    }
+  }
 }
 
 .btn-default.gutter-toggle {
@@ -262,3 +263,37 @@
     color: $gray-darkest;
   }
 }
+
+.edited-text {
+  color: $gray-darkest;
+
+  .author_link {
+    color: $gray-darkest;
+  }
+}
+
+.participants-list {
+  margin: -5px;
+}
+
+.participants-author {
+  display: inline-block;
+  padding: 5px;
+
+  .author_link {
+    display: block;
+  }
+
+  .avatar.avatar-inline {
+    margin: 0;
+  }
+}
+
+.participants-more {
+  margin-top: 5px;
+  margin-left: 5px;
+
+  a {
+    color: #8c8c8c;
+  }
+}
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 7ac4bc468d6c303cfa9e87667834a41939bfd017..6a1d28590c20721746da2d363f7bee78a7d22d15 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -3,7 +3,7 @@
     padding: 10px $gl-padding;
     position: relative;
 
-    .issue-title {
+    .title {
       margin-bottom: 2px;
     }
 
@@ -130,14 +130,14 @@ form.edit-issue {
 }
 
 .issue-closed-by-widget {
-  color: $secondary-text;
+  color: $gl-text-color;
   margin-left: 52px;
 }
 
 .editor-details {
   display: block;
-  
+
   @media (min-width: $screen-sm-min) {
     display: inline-block;
   }
-}
\ No newline at end of file
+}
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index bc41f7d306f842952eefbd76b69468a6f337fa8a..777bcbca5c369ab198642f33d3e42a25df4788ae 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -45,7 +45,7 @@
     .login-heading h3 {
       font-weight: 300;
       line-height: 1.5;
-      margin: 0 0 10px 0;
+      margin: 0 0 10px;
     }
 
     .login-footer {
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 61783ec46aae8499feb10600dc1818b5b812aea9..daf2651425fa6fc18008b447ac2266f7a8c940b4 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -26,7 +26,7 @@
   display: none;
 }
 
-.new_note, .edit_note {
+.new_note, .note-edit-form {
   .note-form-actions {
     margin-top: $gl-padding;
   }
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index d408853cc80c45467d12c38b4d7d462e481c27b5..4bd2016bdcfe3f714e05fa52e0ef131eb8946489 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -100,6 +100,18 @@ ul.notes {
     display: block;
     position: relative;
 
+    &.is-editting {
+      .note-header,
+      .note-text,
+      .edited-text {
+        display: none;
+      }
+
+      .note-edit-form {
+        display: block;
+      }
+    }
+
     .note-body {
       overflow: auto;
 
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index 3831f88f01ecc713cef5292a691a76bacb3a8727..d8c991356af24b803c2b68dd19d08ae183bfd833 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -54,7 +54,7 @@
 }
 
 .account-well {
-  padding: 10px 10px;
+  padding: 10px;
   background-color: $help-well-bg;
   border: 1px solid $help-well-border;
   border-radius: $border-radius-base;
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 6c600c99d51fd25a1d7193e39e4a5eedd15151a1..c68bd673a6751e7122e4babfa6f6e18b3186f87f 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -33,6 +33,13 @@
     .project-settings-dropdown {
       margin-left: 10px;
       display: inline-block;
+
+      .dropdown-menu {
+        left: auto;
+        width: auto;
+        right: 0;
+        max-width: 240px;
+      }
     }
   }
 
@@ -61,28 +68,6 @@
     }
   }
 
-  .project-home-desc {
-    h1 {
-      color: #313236;
-      margin: 0;
-      margin-bottom: 6px;
-      font-size: 23px;
-      font-weight: normal;
-    }
-
-    .visibility-icon {
-      display: inline-block;
-      margin-left: 5px;
-      font-size: 18px;
-      color: $gray;
-    }
-
-    p {
-      padding: 0 $gl-padding;
-      color: #5c5d5e;
-    }
-  }
-
   .project-repo-buttons {
     margin-top: 20px;
     margin-bottom: 0;
@@ -326,7 +311,7 @@ pre.light-well {
 }
 
 .git-empty {
-  margin: 0 7px 0 7px;
+  margin: 0 7px;
 
   h5 {
     color: #5c5d5e;
diff --git a/app/assets/stylesheets/pages/stat_graph.scss b/app/assets/stylesheets/pages/stat_graph.scss
index b9be47e7700273cab16d2ba4c309730941fbd0b8..85a0304196c2307a4ba5840d1c91ca7129098cf1 100644
--- a/app/assets/stylesheets/pages/stat_graph.scss
+++ b/app/assets/stylesheets/pages/stat_graph.scss
@@ -16,7 +16,7 @@
 
 #contributors {
   .contributors-list {
-    margin: 0 0 10px 0;
+    margin: 0 0 10px;
     list-style: none;
     padding: 0;
   }
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index 27970eba159f59b7073aafb230b71616c4f85fca..f983e9829e690690b8892c820d5c05ac3affe6a7 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -14,25 +14,8 @@
 }
 
 .todo-item {
-  font-size: $gl-font-size;
-  padding-left: $gl-avatar-size + $gl-padding-top;
-  color: $secondary-text;
-
-  a {
-    color: #4c4e54;
-  }
-
-  .avatar {
-    margin-left: -($gl-avatar-size + $gl-padding-top);
-  }
-
   .todo-title {
     @include str-truncated(calc(100% - 174px));
-    font-weight: 600;
-
-    .author-name {
-      color: #333;
-    }
   }
 
   .todo-body {
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 73c7c9f687c2396ea7ea25bc00222992eaeb886a..25b5e95583e12bb6f7e4258378a6dec12b6f5cfe 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -41,7 +41,7 @@
       vertical-align: middle;
 
       i, a {
-        color: $gl-link-color;
+        color: $gl-dark-link-color;
       }
 
       img {
diff --git a/app/assets/stylesheets/pages/xterm.scss b/app/assets/stylesheets/pages/xterm.scss
index 8886c1dff56fac22a31c6a510222615f5e5be208..3f28e4029291ed83411b45b380c74f5e3148f00d 100644
--- a/app/assets/stylesheets/pages/xterm.scss
+++ b/app/assets/stylesheets/pages/xterm.scss
@@ -21,19 +21,19 @@
   $l-white: #fff;
 
   .term-bold {
-      font-weight: bold;
+    font-weight: bold;
   }
   .term-italic {
-      font-style: italic;
+    font-style: italic;
   }
   .term-conceal {
-      visibility: hidden;
+    visibility: hidden;
   }
   .term-underline {
-      text-decoration: underline;
+    text-decoration: underline;
   }
   .term-cross {
-      text-decoration: line-through;
+    text-decoration: line-through;
   }
 
   .term-fg-black {
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 04a99d8c84a288fadc1e095d759bfeffabed754b..ed9f6031389208126749bcf25523bbaebaf2f15b 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -61,6 +61,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
       :session_expire_delay,
       :default_project_visibility,
       :default_snippet_visibility,
+      :default_group_visibility,
       :restricted_signup_domains_raw,
       :version_check_enabled,
       :admin_notification_email,
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 668396a0f20d2404674cbd93a2c939152bd00b7b..a6db4690df03f3993c46348b86fcb50dd7ca6425 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -5,12 +5,12 @@ class Admin::GroupsController < Admin::ApplicationController
     @groups = Group.all
     @groups = @groups.sort(@sort = params[:sort])
     @groups = @groups.search(params[:name]) if params[:name].present?
-    @groups = @groups.page(params[:page]).per(PER_PAGE)
+    @groups = @groups.page(params[:page])
   end
 
   def show
-    @members = @group.members.order("access_level DESC").page(params[:members_page]).per(PER_PAGE)
-    @projects = @group.projects.page(params[:projects_page]).per(PER_PAGE)
+    @members = @group.members.order("access_level DESC").page(params[:members_page])
+    @projects = @group.projects.page(params[:projects_page])
   end
 
   def new
@@ -59,6 +59,6 @@ class Admin::GroupsController < Admin::ApplicationController
   end
 
   def group_params
-    params.require(:group).permit(:name, :description, :path, :avatar)
+    params.require(:group).permit(:name, :description, :path, :avatar, :visibility_level)
   end
 end
diff --git a/app/controllers/admin/labels_controller.rb b/app/controllers/admin/labels_controller.rb
index d79ce2b10fedc36054d5092d2c31bb20f1f9056f..d496f08a598ca34dd248ccb5e648cd89d3807ecd 100644
--- a/app/controllers/admin/labels_controller.rb
+++ b/app/controllers/admin/labels_controller.rb
@@ -2,7 +2,7 @@ class Admin::LabelsController < Admin::ApplicationController
   before_action :set_label, only: [:show, :edit, :update, :destroy]
 
   def index
-    @labels = Label.templates.page(params[:page]).per(PER_PAGE)
+    @labels = Label.templates.page(params[:page])
   end
 
   def show
diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb
index ae1de06b983ce58ba5690eaa83b553eee03d56bf..4089091d569ffbee6067a6afb7d92a411e633f31 100644
--- a/app/controllers/admin/projects_controller.rb
+++ b/app/controllers/admin/projects_controller.rb
@@ -1,7 +1,6 @@
 class Admin::ProjectsController < Admin::ApplicationController
   before_action :project, only: [:show, :transfer]
   before_action :group, only: [:show, :transfer]
-  before_action :repository, only: [:show, :transfer]
 
   def index
     @projects = Project.all
@@ -12,15 +11,15 @@ class Admin::ProjectsController < Admin::ApplicationController
     @projects = @projects.non_archived unless params[:with_archived].present?
     @projects = @projects.search(params[:name]) if params[:name].present?
     @projects = @projects.sort(@sort = params[:sort])
-    @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(PER_PAGE)
+    @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page])
   end
 
   def show
     if @group
-      @group_members = @group.members.order("access_level DESC").page(params[:group_members_page]).per(PER_PAGE)
+      @group_members = @group.members.order("access_level DESC").page(params[:group_members_page])
     end
 
-    @project_members = @project.project_members.page(params[:project_members_page]).per(PER_PAGE)
+    @project_members = @project.project_members.page(params[:project_members_page])
   end
 
   def transfer
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 1f55b18e0b160b8c6c11f5eaa2202f9b03d20d79..c81cb85dc1befddd12fdd311ffd6203df6ce1ac6 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -6,8 +6,6 @@ class ApplicationController < ActionController::Base
   include GitlabRoutingHelper
   include PageLayoutHelper
 
-  PER_PAGE = 20
-
   before_action :authenticate_user_from_token!
   before_action :authenticate_user!
   before_action :validate_user_service_ticket!
@@ -25,7 +23,6 @@ class ApplicationController < ActionController::Base
 
   helper_method :abilities, :can?, :current_application_settings
   helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?
-  helper_method :repository, :can_collaborate_with_project?
 
   rescue_from Encoding::CompatibilityError do |exception|
     log_exception(exception)
@@ -118,47 +115,6 @@ class ApplicationController < ActionController::Base
     abilities.allowed?(object, action, subject)
   end
 
-  def project
-    unless @project
-      namespace = params[:namespace_id]
-      id = params[:project_id] || params[:id]
-
-      # Redirect from
-      #   localhost/group/project.git
-      # to
-      #   localhost/group/project
-      #
-      if id =~ /\.git\Z/
-        redirect_to request.original_url.gsub(/\.git\/?\Z/, '') and return
-      end
-
-      project_path = "#{namespace}/#{id}"
-      @project = Project.find_with_namespace(project_path)
-
-      if @project and can?(current_user, :read_project, @project)
-        if @project.path_with_namespace != project_path
-          redirect_to request.original_url.gsub(project_path, @project.path_with_namespace) and return
-        end
-        @project
-      elsif current_user.nil?
-        @project = nil
-        authenticate_user!
-      else
-        @project = nil
-        render_404 and return
-      end
-    end
-    @project
-  end
-
-  def repository
-    @repository ||= project.repository
-  end
-
-  def authorize_project!(action)
-    return access_denied! unless can?(current_user, action, project)
-  end
-
   def access_denied!
     render "errors/access_denied", layout: "errors", status: 404
   end
@@ -167,14 +123,6 @@ class ApplicationController < ActionController::Base
     render "errors/git_not_found.html", layout: "errors", status: 404
   end
 
-  def method_missing(method_sym, *arguments, &block)
-    if method_sym.to_s =~ /\Aauthorize_(.*)!\z/
-      authorize_project!($1.to_sym)
-    else
-      super
-    end
-  end
-
   def render_403
     head :forbidden
   end
@@ -183,10 +131,6 @@ class ApplicationController < ActionController::Base
     render file: Rails.root.join("public", "404"), layout: false, status: "404"
   end
 
-  def require_non_empty_project
-    redirect_to @project if @project.empty_repo?
-  end
-
   def no_cache_headers
     response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
     response.headers["Pragma"] = "no-cache"
@@ -412,13 +356,6 @@ class ApplicationController < ActionController::Base
     current_user.nil? && root_path == request.path
   end
 
-  def can_collaborate_with_project?(project = nil)
-    project ||= @project
-
-    can?(current_user, :push_code, project) ||
-      (current_user && current_user.already_forked?(project))
-  end
-
   private
 
   def set_default_sort
diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb
index 77c8dafc012eec88854d5efc7bf763b7b4406d2b..81ba58ce49c4168b3425e36b24462a27a565a5b5 100644
--- a/app/controllers/autocomplete_controller.rb
+++ b/app/controllers/autocomplete_controller.rb
@@ -7,7 +7,7 @@ class AutocompleteController < ApplicationController
     @users = @users.search(params[:search]) if params[:search].present?
     @users = @users.active
     @users = @users.reorder(:name)
-    @users = @users.page(params[:page]).per(PER_PAGE)
+    @users = @users.page(params[:page])
 
     if params[:search].blank?
       # Include current user if available to filter by "Me"
diff --git a/app/controllers/concerns/global_milestones.rb b/app/controllers/concerns/global_milestones.rb
index 3e4c0e63601d4296333a7327c7973ed4af5b661a..54ea1e454fc6fa17651a8207ba7c842e58fad297 100644
--- a/app/controllers/concerns/global_milestones.rb
+++ b/app/controllers/concerns/global_milestones.rb
@@ -6,7 +6,7 @@ module GlobalMilestones
     @milestones = MilestonesFinder.new.execute(@projects, params)
     @milestones = GlobalMilestone.build_collection(@milestones)
     @milestones = @milestones.sort_by { |x| x.due_date.nil? ? epoch : x.due_date }
-    @milestones = Kaminari.paginate_array(@milestones).page(params[:page]).per(ApplicationController::PER_PAGE)
+    @milestones = Kaminari.paginate_array(@milestones).page(params[:page])
   end
 
   def milestone
diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f40b62446e5ecbd223e5c7b282e25a4d9c64d31e
--- /dev/null
+++ b/app/controllers/concerns/issuable_actions.rb
@@ -0,0 +1,23 @@
+module IssuableActions
+  extend ActiveSupport::Concern
+
+  included do
+    before_action :authorize_destroy_issuable!, only: :destroy
+  end
+
+  def destroy
+    issuable.destroy
+
+    name = issuable.class.name.titleize.downcase
+    flash[:notice] = "The #{name} was successfully deleted."
+    redirect_to polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable.class])
+  end
+
+  private
+
+  def authorize_destroy_issuable!
+    unless current_user.can?(:"destroy_#{issuable.to_ability_name}", issuable)
+      return access_denied!
+    end
+  end
+end
diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb
index ef8e74a46415c8b9aef43d23644839b72a2ed2dd..4feabc32b1cd89520c11ba5df5a772fe24883c6d 100644
--- a/app/controllers/concerns/issues_action.rb
+++ b/app/controllers/concerns/issues_action.rb
@@ -3,7 +3,7 @@ module IssuesAction
 
   def issues
     @issues = get_issues_collection.non_archived
-    @issues = @issues.page(params[:page]).per(ApplicationController::PER_PAGE)
+    @issues = @issues.page(params[:page])
     @issues = @issues.preload(:author, :project)
 
     @label = @issuable_finder.labels.first
diff --git a/app/controllers/concerns/merge_requests_action.rb b/app/controllers/concerns/merge_requests_action.rb
index 9c49596bd0be6e511b5353bcd2fe63d06682c0fe..06a6b065e7e984eac743f9545d3701a288564397 100644
--- a/app/controllers/concerns/merge_requests_action.rb
+++ b/app/controllers/concerns/merge_requests_action.rb
@@ -3,7 +3,7 @@ module MergeRequestsAction
 
   def merge_requests
     @merge_requests = get_merge_requests_collection.non_archived
-    @merge_requests = @merge_requests.page(params[:page]).per(ApplicationController::PER_PAGE)
+    @merge_requests = @merge_requests.page(params[:page])
     @merge_requests = @merge_requests.preload(:author, :target_project)
 
     @label = @issuable_finder.labels.first
diff --git a/app/controllers/dashboard/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb
index 3bc94ff218747c10c310952971dd61dbf8be6f99..71ba6153021fb1919044288de79a4a8a7e9f3e16 100644
--- a/app/controllers/dashboard/groups_controller.rb
+++ b/app/controllers/dashboard/groups_controller.rb
@@ -1,5 +1,5 @@
 class Dashboard::GroupsController < Dashboard::ApplicationController
   def index
-    @group_members = current_user.group_members.page(params[:page]).per(PER_PAGE)
+    @group_members = current_user.group_members.page(params[:page])
   end
 end
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index 0e8b63872ca1cc9bc8919802bb320ab676da40ab..71acc244a9170b488423318b2dd091c4cce98295 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -8,7 +8,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
     @projects = filter_projects(@projects)
     @projects = @projects.includes(:namespace)
     @projects = @projects.sort(@sort = params[:sort])
-    @projects = @projects.page(params[:page]).per(PER_PAGE)
+    @projects = @projects.page(params[:page])
 
     @last_push = current_user.recent_push
 
@@ -32,7 +32,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
     @projects = filter_projects(@projects)
     @projects = @projects.includes(:namespace, :forked_from_project, :tags)
     @projects = @projects.sort(@sort = params[:sort])
-    @projects = @projects.page(params[:page]).per(PER_PAGE)
+    @projects = @projects.page(params[:page])
 
     @last_push = current_user.recent_push
     @groups = []
diff --git a/app/controllers/dashboard/snippets_controller.rb b/app/controllers/dashboard/snippets_controller.rb
index b3594d82530b09d06bfb97e2ec7189b42bf98313..bcfdbe14be968b004344c3afbcdf8d849bb68fac 100644
--- a/app/controllers/dashboard/snippets_controller.rb
+++ b/app/controllers/dashboard/snippets_controller.rb
@@ -6,6 +6,6 @@ class Dashboard::SnippetsController < Dashboard::ApplicationController
       user: current_user,
       scope: params[:scope]
     )
-    @snippets = @snippets.page(params[:page]).per(PER_PAGE)
+    @snippets = @snippets.page(params[:page])
   end
 end
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index 43cf8fa71af4d468ebf0a30d8df7e13c879cf508..5abf97342c3ad4fe253600c454bafb958997a10e 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -1,25 +1,34 @@
 class Dashboard::TodosController < Dashboard::ApplicationController
-  before_action :find_todos, only: [:index, :destroy_all]
+  before_action :find_todos, only: [:index, :destroy, :destroy_all]
 
   def index
-    @todos = @todos.page(params[:page]).per(PER_PAGE)
+    @todos = @todos.page(params[:page])
   end
 
   def destroy
-    todo.done!
+    todo.done
+
+    todo_notice = 'Todo was successfully marked as done.'
 
     respond_to do |format|
-      format.html { redirect_to dashboard_todos_path, notice: 'Todo was successfully marked as done.' }
+      format.html { redirect_to dashboard_todos_path, notice: todo_notice }
       format.js { render nothing: true }
+      format.json do
+        render json: { count: @todos.size, done_count: current_user.todos.done.count }
+      end
     end
   end
 
   def destroy_all
-    @todos.each(&:done!)
+    @todos.each(&:done)
 
     respond_to do |format|
       format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' }
       format.js { render nothing: true }
+      format.json do
+        find_todos
+        render json: { count: @todos.size, done_count: current_user.todos.done.count }
+      end
     end
   end
 
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index 139e40db180ffee38c873eab2cccecc95d84b957..b538c7d160822a270794ce954889f92b16d19ae2 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -3,7 +3,7 @@ class DashboardController < Dashboard::ApplicationController
   include MergeRequestsAction
 
   before_action :event_filter, only: :activity
-  before_action :projects, only: [:issues, :merge_requests]
+  before_action :projects, only: [:issues, :merge_requests, :labels, :milestones]
 
   respond_to :html
 
@@ -20,6 +20,29 @@ class DashboardController < Dashboard::ApplicationController
     end
   end
 
+  def labels
+    labels = Label.where(project_id: @projects).select(:title, :color).uniq(:title)
+
+    respond_to do |format|
+      format.json do
+        render json: labels
+      end
+    end
+  end
+
+  def milestones
+    milestones = Milestone.where(project_id: @projects).active
+    epoch = DateTime.parse('1970-01-01')
+    grouped_milestones = GlobalMilestone.build_collection(milestones)
+    grouped_milestones = grouped_milestones.sort_by { |x| x.due_date.nil? ? epoch : x.due_date }
+
+    respond_to do |format|
+      format.json do
+        render json: grouped_milestones
+      end
+    end
+  end
+
   protected
 
   def load_events
diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb
index a9bf4321f735a93efde68384d0e03e3e79feb410..a962f9a0937f16b4dd08aaef60c1acd19ebc4c85 100644
--- a/app/controllers/explore/groups_controller.rb
+++ b/app/controllers/explore/groups_controller.rb
@@ -1,8 +1,8 @@
 class Explore::GroupsController < Explore::ApplicationController
   def index
-    @groups = Group.order_id_desc
+    @groups = GroupsFinder.new.execute(current_user)
     @groups = @groups.search(params[:search]) if params[:search].present?
     @groups = @groups.sort(@sort = params[:sort])
-    @groups = @groups.page(params[:page]).per(PER_PAGE)
+    @groups = @groups.page(params[:page])
   end
 end
diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb
index 8271ca87436a9891c56e9c6b42f4358a60b45280..88a0c18180be3240e312a28513713d4bc8735c4e 100644
--- a/app/controllers/explore/projects_controller.rb
+++ b/app/controllers/explore/projects_controller.rb
@@ -8,7 +8,7 @@ class Explore::ProjectsController < Explore::ApplicationController
     @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
     @projects = filter_projects(@projects)
     @projects = @projects.sort(@sort = params[:sort])
-    @projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE)
+    @projects = @projects.includes(:namespace).page(params[:page])
 
     respond_to do |format|
       format.html
@@ -23,7 +23,7 @@ class Explore::ProjectsController < Explore::ApplicationController
   def trending
     @projects = TrendingProjectsFinder.new.execute(current_user)
     @projects = filter_projects(@projects)
-    @projects = @projects.page(params[:page]).per(PER_PAGE)
+    @projects = @projects.page(params[:page])
 
     respond_to do |format|
       format.html
@@ -39,7 +39,7 @@ class Explore::ProjectsController < Explore::ApplicationController
     @projects = ProjectsFinder.new.execute(current_user)
     @projects = filter_projects(@projects)
     @projects = @projects.reorder('star_count DESC')
-    @projects = @projects.page(params[:page]).per(PER_PAGE)
+    @projects = @projects.page(params[:page])
 
     respond_to do |format|
       format.html
diff --git a/app/controllers/explore/snippets_controller.rb b/app/controllers/explore/snippets_controller.rb
index b70ac51d06e83690eccebe83226c1ca8ffd9326b..28760c3f84b331f9ca06102428913baf5660afd4 100644
--- a/app/controllers/explore/snippets_controller.rb
+++ b/app/controllers/explore/snippets_controller.rb
@@ -1,6 +1,6 @@
 class Explore::SnippetsController < Explore::ApplicationController
   def index
     @snippets = SnippetsFinder.new.execute(current_user, filter: :all)
-    @snippets = @snippets.page(params[:page]).per(PER_PAGE)
+    @snippets = @snippets.page(params[:page])
   end
 end
diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb
index be801858eafed5db195b01c1ede0a3bf83175282..949b4a6c25ae3c253c838ff91e6307ad823fd9cc 100644
--- a/app/controllers/groups/application_controller.rb
+++ b/app/controllers/groups/application_controller.rb
@@ -1,21 +1,32 @@
 class Groups::ApplicationController < ApplicationController
   layout 'group'
+
+  skip_before_action :authenticate_user!
   before_action :group
 
   private
 
   def group
-    @group ||= Group.find_by(path: params[:group_id])
-  end
+    unless @group
+      id = params[:group_id] || params[:id]
+      @group = Group.find_by(path: id)
+
+      unless @group && can?(current_user, :read_group, @group)
+        @group = nil
 
-  def authorize_read_group!
-    unless @group and can?(current_user, :read_group, @group)
-      if current_user.nil?
-        return authenticate_user!
-      else
-        return render_404
+        if current_user.nil?
+          authenticate_user!
+        else
+          render_404
+        end
       end
     end
+
+    @group
+  end
+
+  def group_projects
+    @projects ||= GroupProjectsFinder.new(group).execute(current_user)
   end
 
   def authorize_admin_group!
diff --git a/app/controllers/groups/avatars_controller.rb b/app/controllers/groups/avatars_controller.rb
index 76c87366baad198859a3dd9dfd77849c45437fb2..ad2c20b42dbc7d1c2d25a87617c71fb7319b6359 100644
--- a/app/controllers/groups/avatars_controller.rb
+++ b/app/controllers/groups/avatars_controller.rb
@@ -1,4 +1,6 @@
 class Groups::AvatarsController < Groups::ApplicationController
+  before_action :authorize_admin_group!
+
   def destroy
     @group.remove_avatar!
     @group.save
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index 0e902c4bb4347958201b5430b7c6ece70dc068c5..d5ef33888c692966935a60e05167bee44720f491 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -1,8 +1,5 @@
 class Groups::GroupMembersController < Groups::ApplicationController
-  skip_before_action :authenticate_user!, only: [:index]
-
   # Authorize
-  before_action :authorize_read_group!
   before_action :authorize_admin_group_member!, except: [:index, :leave]
 
   def index
diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb
index 0c2a350bc39ef24b1ab809d4ee5d75d4d6c4cfc1..0028f072d5b568e42a0d3fcabb883f9efebae329 100644
--- a/app/controllers/groups/milestones_controller.rb
+++ b/app/controllers/groups/milestones_controller.rb
@@ -1,10 +1,10 @@
 class Groups::MilestonesController < Groups::ApplicationController
   include GlobalMilestones
 
-  before_action :projects
+  before_action :group_projects
   before_action :milestones, only: [:index]
   before_action :milestone, only: [:show, :update]
-  before_action :authorize_group_milestone!, only: [:create, :update]
+  before_action :authorize_admin_milestones!, only: [:new, :create, :update]
 
   def index
   end
@@ -17,7 +17,7 @@ class Groups::MilestonesController < Groups::ApplicationController
     project_ids = params[:milestone][:project_ids]
     title = milestone_params[:title]
 
-    @group.projects.where(id: project_ids).each do |project|
+    @projects.where(id: project_ids).each do |project|
       Milestones::CreateService.new(project, current_user, milestone_params).execute
     end
 
@@ -37,7 +37,7 @@ class Groups::MilestonesController < Groups::ApplicationController
 
   private
 
-  def authorize_group_milestone!
+  def authorize_admin_milestones!
     return render_404 unless can?(current_user, :admin_milestones, group)
   end
 
@@ -48,8 +48,4 @@ class Groups::MilestonesController < Groups::ApplicationController
   def milestone_path(title)
     group_milestone_path(@group, title.to_slug.to_s, title: title)
   end
-
-  def projects
-    @projects ||= @group.projects
-  end
 end
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 06c5c8be9a5703e5a7ec43ce91cfbc712f41fa91..c1adc999567019ee6d0ab7aa945cbbeb0d445ed9 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -5,16 +5,15 @@ class GroupsController < Groups::ApplicationController
 
   respond_to :html
 
-  skip_before_action :authenticate_user!, only: [:index, :show, :issues, :merge_requests]
+  before_action :authenticate_user!, only: [:new, :create]
   before_action :group, except: [:index, :new, :create]
 
   # Authorize
-  before_action :authorize_read_group!, except: [:index, :show, :new, :create, :autocomplete]
   before_action :authorize_admin_group!, only: [:edit, :update, :destroy, :projects]
   before_action :authorize_create_group!, only: [:new, :create]
 
   # Load group projects
-  before_action :load_projects, except: [:index, :new, :create, :projects, :edit, :update, :autocomplete]
+  before_action :group_projects, only: [:show, :projects, :activity, :issues, :merge_requests]
   before_action :event_filter, only: [:activity]
 
   layout :determine_layout
@@ -28,11 +27,9 @@ class GroupsController < Groups::ApplicationController
   end
 
   def create
-    @group = Group.new(group_params)
-    @group.name = @group.path.dup unless @group.name
+    @group = Groups::CreateService.new(current_user, group_params).execute
 
-    if @group.save
-      @group.add_owner(current_user)
+    if @group.persisted?
       redirect_to @group, notice: "Group '#{@group.name}' was successfully created."
     else
       render action: "new"
@@ -41,12 +38,13 @@ class GroupsController < Groups::ApplicationController
 
   def show
     @last_push = current_user.recent_push if current_user
+
     @projects = @projects.includes(:namespace)
     @projects = filter_projects(@projects)
     @projects = @projects.sort(@sort = params[:sort])
-    @projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
+    @projects = @projects.page(params[:page]) if params[:filter_projects].blank?
 
-    @shared_projects = @group.shared_projects
+    @shared_projects = GroupProjectsFinder.new(group, only_shared: true).execute(current_user)
 
     respond_to do |format|
       format.html
@@ -83,7 +81,7 @@ class GroupsController < Groups::ApplicationController
   end
 
   def update
-    if @group.update_attributes(group_params)
+    if Groups::UpdateService.new(@group, current_user, group_params).execute
       redirect_to edit_group_path(@group), notice: "Group '#{@group.name}' was successfully updated."
     else
       render action: "edit"
@@ -98,26 +96,6 @@ class GroupsController < Groups::ApplicationController
 
   protected
 
-  def group
-    @group ||= Group.find_by(path: params[:id])
-    @group || render_404
-  end
-
-  def load_projects
-    @projects ||= ProjectsFinder.new.execute(current_user, group: group).sorted_by_activity
-  end
-
-  # Dont allow unauthorized access to group
-  def authorize_read_group!
-    unless @group and (@projects.present? or can?(current_user, :read_group, @group))
-      if current_user.nil?
-        return authenticate_user!
-      else
-        return render_404
-      end
-    end
-  end
-
   def authorize_create_group!
     unless can?(current_user, :create_group, nil)
       return render_404
@@ -135,7 +113,7 @@ class GroupsController < Groups::ApplicationController
   end
 
   def group_params
-    params.require(:group).permit(:name, :description, :path, :avatar, :public, :share_with_group_lock)
+    params.require(:group).permit(:name, :description, :path, :avatar, :public, :visibility_level, :share_with_group_lock)
   end
 
   def load_events
diff --git a/app/controllers/namespaces_controller.rb b/app/controllers/namespaces_controller.rb
index 282012c60a10ef0161aa5261a219a2a83a466c31..5a94dcb0dbda623f540ddf128c0e1d25c9829bf9 100644
--- a/app/controllers/namespaces_controller.rb
+++ b/app/controllers/namespaces_controller.rb
@@ -14,7 +14,7 @@ class NamespacesController < ApplicationController
 
     if user
       redirect_to user_path(user)
-    elsif group
+    elsif group && can?(current_user, :read_group, namespace)
       redirect_to group_path(group)
     elsif current_user.nil?
       authenticate_user!
diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb
index 24025d8c723f27146a4676685071024e19029903..c721dca58d93f0133c45e5264cbc98c310e99198 100644
--- a/app/controllers/oauth/authorizations_controller.rb
+++ b/app/controllers/oauth/authorizations_controller.rb
@@ -7,6 +7,7 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
     if pre_auth.authorizable?
       if skip_authorization? || matching_token?
         auth = authorization.authorize
+        session.delete(:user_return_to)
         redirect_to auth.redirect_uri
       else
         render "doorkeeper/authorizations/new"
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 30e2886caca1d4f64721a45b087f92304ac7ae8e..c5fa756d02bb8098603cb9714395a1e7eccf1cc7 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -35,8 +35,7 @@ class ProfilesController < Profiles::ApplicationController
   def audit_log
     @events = AuditEvent.where(entity_type: "User", entity_id: current_user.id).
       order("created_at DESC").
-      page(params[:page]).
-      per(PER_PAGE)
+      page(params[:page])
   end
 
   def update_username
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index a326bc582158240f4ad0759dffc57092900cf72e..657ee94cfd768ef7cea34d3ab55f7d667194e6a9 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -1,20 +1,74 @@
 class Projects::ApplicationController < ApplicationController
+  skip_before_action :authenticate_user!
   before_action :project
   before_action :repository
   layout 'project'
 
-  def authenticate_user!
-    # Restrict access to Projects area only
-    # for non-signed users
-    if !current_user
+  helper_method :repository, :can_collaborate_with_project?
+
+  private
+
+  def project
+    unless @project
+      namespace = params[:namespace_id]
       id = params[:project_id] || params[:id]
-      project_with_namespace = "#{params[:namespace_id]}/#{id}"
-      @project = Project.find_with_namespace(project_with_namespace)
 
-      return if @project && @project.public?
+      # Redirect from
+      #   localhost/group/project.git
+      # to
+      #   localhost/group/project
+      #
+      if id =~ /\.git\Z/
+        redirect_to request.original_url.gsub(/\.git\/?\Z/, '')
+        return
+      end
+
+      project_path = "#{namespace}/#{id}"
+      @project = Project.find_with_namespace(project_path)
+
+      if @project && can?(current_user, :read_project, @project)
+        if @project.path_with_namespace != project_path
+          redirect_to request.original_url.gsub(project_path, @project.path_with_namespace)
+        end
+      else
+        @project = nil
+
+        if current_user.nil?
+          authenticate_user!
+        else
+          render_404
+        end
+      end
+    end
+
+    @project
+  end
+
+  def repository
+    @repository ||= project.repository
+  end
+
+  def can_collaborate_with_project?(project = nil)
+    project ||= @project
+
+    can?(current_user, :push_code, project) ||
+      (current_user && current_user.already_forked?(project))
+  end
+
+  def authorize_project!(action)
+    return access_denied! unless can?(current_user, action, project)
+  end
+
+  def method_missing(method_sym, *arguments, &block)
+    if method_sym.to_s =~ /\Aauthorize_(.*)!\z/
+      authorize_project!($1.to_sym)
+    else
+      super
     end
+  end
 
-    super
+  def require_non_empty_project
+    redirect_to namespace_project_path(@project.namespace, @project) if @project.empty_repo?
   end
 
   def require_branch_head
@@ -26,8 +80,6 @@ class Projects::ApplicationController < ApplicationController
     end
   end
 
-  private
-
   def apply_diff_view_cookie!
     view = params[:view] || cookies[:diff_view]
     cookies.permanent[:diff_view] = params[:view] = view if view
diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb
index a6bebc46b061225c6799958d2679f155e9c04f12..72921b3aa145303f0246614c90484fe686b08558 100644
--- a/app/controllers/projects/avatars_controller.rb
+++ b/app/controllers/projects/avatars_controller.rb
@@ -1,7 +1,7 @@
 class Projects::AvatarsController < Projects::ApplicationController
   include BlobHelper
 
-  before_action :project
+  before_action :authorize_admin_project!, only: [:destroy]
 
   def show
     @blob = @repository.blob_at_branch('master', @project.avatar_in_git)
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index 43ea717cbd2ff52140ecf50c02d281df68e103d6..c0a5373492182bb4e0ada112d6fe8c8dcdacbdc0 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -8,7 +8,7 @@ class Projects::BranchesController < Projects::ApplicationController
   def index
     @sort = params[:sort] || 'name'
     @branches = @repository.branches_sorted_by(@sort)
-    @branches = Kaminari.paginate_array(@branches).page(params[:page]).per(PER_PAGE)
+    @branches = Kaminari.paginate_array(@branches).page(params[:page])
 
     @max_commits = @branches.reduce(0) do |memo, branch|
       diverging_commit_counts = repository.diverging_commit_counts(branch)
diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb
index a1b8632df98227514695387007c44275b1c299f0..ade01c706a79348caa05f598ce05d368ca65299f 100644
--- a/app/controllers/projects/forks_controller.rb
+++ b/app/controllers/projects/forks_controller.rb
@@ -15,7 +15,7 @@ class Projects::ForksController < Projects::ApplicationController
 
     @sort  = params[:sort] || 'id_desc'
     @forks = @forks.search(params[:filter_projects]) if params[:filter_projects].present?
-    @forks = @forks.order_by(@sort).page(params[:page]).per(PER_PAGE)
+    @forks = @forks.order_by(@sort).page(params[:page])
 
     respond_to do |format|
       format.html
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index aa7a178dcf4da748c82897d6e4fa4edb7cb27d7a..877b39c9b1bf131a4483e9cb05a3a4172c13a7fa 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -1,11 +1,12 @@
 class Projects::IssuesController < Projects::ApplicationController
   include ToggleSubscriptionAction
+  include IssuableActions
 
   before_action :module_enabled
   before_action :issue, only: [:edit, :update, :show]
 
   # Allow read any issue
-  before_action :authorize_read_issue!
+  before_action :authorize_read_issue!, only: [:show]
 
   # Allow write(create) issue
   before_action :authorize_create_issue!, only: [:new, :create]
@@ -33,7 +34,7 @@ class Projects::IssuesController < Projects::ApplicationController
       end
     end
 
-    @issues = @issues.page(params[:page]).per(PER_PAGE)
+    @issues = @issues.page(params[:page])
     @label = @project.labels.find_by(title: params[:label_name])
 
     respond_to do |format|
@@ -90,6 +91,12 @@ class Projects::IssuesController < Projects::ApplicationController
   def update
     @issue = Issues::UpdateService.new(project, current_user, issue_params).execute(issue)
 
+    if params[:move_to_project_id].to_i > 0
+      new_project = Project.find(params[:move_to_project_id])
+      move_service = Issues::MoveService.new(project, current_user)
+      @issue = move_service.execute(@issue, new_project)
+    end
+
     respond_to do |format|
       format.js
       format.html do
@@ -127,6 +134,11 @@ class Projects::IssuesController < Projects::ApplicationController
                end
   end
   alias_method :subscribable_resource, :issue
+  alias_method :issuable, :issue
+
+  def authorize_read_issue!
+    return render_404 unless can?(current_user, :read_issue, @issue)
+  end
 
   def authorize_update_issue!
     return render_404 unless can?(current_user, :update_issue, @issue)
@@ -158,7 +170,7 @@ class Projects::IssuesController < Projects::ApplicationController
 
   def issue_params
     params.require(:issue).permit(
-      :title, :assignee_id, :position, :description,
+      :title, :assignee_id, :position, :description, :confidential,
       :milestone_id, :state_event, :task_num, label_ids: []
     )
   end
diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb
index 40d8098690aa1cedbbccc9f412307ca0ecd3c15f..ff771ea6d9cc444e511dc5aeb637d3fec2303534 100644
--- a/app/controllers/projects/labels_controller.rb
+++ b/app/controllers/projects/labels_controller.rb
@@ -11,7 +11,14 @@ class Projects::LabelsController < Projects::ApplicationController
   respond_to :js, :html
 
   def index
-    @labels = @project.labels.page(params[:page]).per(PER_PAGE)
+    @labels = @project.labels.page(params[:page])
+
+    respond_to do |format|
+      format.html
+      format.json do
+        render json: @project.labels
+      end
+    end
   end
 
   def new
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 61b82c9db466d4ff793ed53eafeac60e7ab410c1..b830d7777526ebcd941a93504ee8954d1db8d9c7 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -1,11 +1,12 @@
 class Projects::MergeRequestsController < Projects::ApplicationController
   include ToggleSubscriptionAction
   include DiffHelper
+  include IssuableActions
 
   before_action :module_enabled
   before_action :merge_request, only: [
     :edit, :update, :show, :diffs, :commits, :builds, :merge, :merge_check,
-    :ci_status, :cancel_merge_when_build_succeeds
+    :ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip
   ]
   before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits, :builds]
   before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds]
@@ -20,7 +21,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
   before_action :authorize_create_merge_request!, only: [:new, :create]
 
   # Allow modify merge_request
-  before_action :authorize_update_merge_request!, only: [:close, :edit, :update, :sort]
+  before_action :authorize_update_merge_request!, only: [:close, :edit, :update, :remove_wip, :sort]
 
   def index
     terms = params['issue_search']
@@ -34,7 +35,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
       end
     end
 
-    @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE)
+    @merge_requests = @merge_requests.page(params[:page])
     @merge_requests = @merge_requests.preload(:target_project)
 
     @label = @project.labels.find_by(title: params[:label_name])
@@ -164,6 +165,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController
     end
   end
 
+  def remove_wip
+    MergeRequests::UpdateService.new(project, current_user, title: @merge_request.wipless_title).execute(@merge_request)
+
+    redirect_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request),
+      notice: "The merge request can now be merged."
+  end
+
   def merge_check
     @merge_request.check_if_can_be_merged
 
@@ -248,6 +256,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
     @merge_request ||= @project.merge_requests.find_by!(iid: params[:id])
   end
   alias_method :subscribable_resource, :merge_request
+  alias_method :issuable, :merge_request
 
   def closes_issues
     @closes_issues ||= @merge_request.closes_issues
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index da46731d945eacc310ebe013498c4ff23dd13be2..b2e974eff1762b876394b53f413b4c0ab8b04c11 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -19,7 +19,15 @@ class Projects::MilestonesController < Projects::ApplicationController
       end
 
     @milestones = @milestones.includes(:project)
-    @milestones = @milestones.page(params[:page]).per(PER_PAGE)
+
+    respond_to do |format|
+      format.html do
+        @milestones = @milestones.page(params[:page])
+      end
+      format.json do
+        render json: @milestones
+      end
+    end
   end
 
   def new
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 92b0caa2efb3f50097917b5a248fdd6c50ace19d..b578b419a46d4c041188d20cc46b5b0589a74ace 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -21,7 +21,7 @@ class Projects::SnippetsController < Projects::ApplicationController
       filter: :by_project,
       project: @project
     })
-    @snippets = @snippets.page(params[:page]).per(PER_PAGE)
+    @snippets = @snippets.page(params[:page])
   end
 
   def new
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index e580487a2c6dd72405009c2b359e5b7860098a5b..46b242aa5ff5aeb70e81d0e172d7266a573c850a 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -7,7 +7,7 @@ class Projects::TagsController < Projects::ApplicationController
 
   def index
     sorted = VersionSorter.rsort(@repository.tag_names)
-    @tags = Kaminari.paginate_array(sorted).page(params[:page]).per(PER_PAGE)
+    @tags = Kaminari.paginate_array(sorted).page(params[:page])
     @releases = project.releases.where(tag: @tags)
   end
 
diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb
index e1fe7ea21143fcc6d3131cff79026d9f6f2ebafb..caed064dfbc11ec76715f21004b418b400e86cbc 100644
--- a/app/controllers/projects/uploads_controller.rb
+++ b/app/controllers/projects/uploads_controller.rb
@@ -1,7 +1,9 @@
 class Projects::UploadsController < Projects::ApplicationController
-  skip_before_action :authenticate_user!, :reject_blocked!, :project,
+  skip_before_action :reject_blocked!, :project,
     :repository, if: -> { action_name == 'show' && image? }
 
+  before_action :authorize_upload_file!, only: [:create]
+
   def create
     link_to_file = ::Projects::UploadService.new(project, params[:file]).
       execute
@@ -26,6 +28,8 @@ class Projects::UploadsController < Projects::ApplicationController
     send_file uploader.file.path, disposition: disposition
   end
 
+  private
+
   def uploader
     return @uploader if defined?(@uploader)
 
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index 88fccfed5099e837b026783e8ff1ee25f5fb74c0..02ceb8f433429716ff9464b428cc9248292cec8a 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -7,7 +7,7 @@ class Projects::WikisController < Projects::ApplicationController
   before_action :load_project_wiki
 
   def pages
-    @wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page]).per(PER_PAGE)
+    @wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page])
   end
 
   def show
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 36f37221c584f68fd5f96b1679777e24ed09cf2e..928817ba811e6de19e97bc62ffc8487935fe550e 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -1,7 +1,7 @@
-class ProjectsController < ApplicationController
+class ProjectsController < Projects::ApplicationController
   include ExtractsPath
 
-  skip_before_action :authenticate_user!, only: [:show, :activity]
+  before_action :authenticate_user!, except: [:show, :activity]
   before_action :project, except: [:new, :create]
   before_action :repository, except: [:new, :create]
   before_action :assign_ref_vars, :tree, only: [:show], if: :repo_exists?
@@ -134,7 +134,7 @@ class ProjectsController < ApplicationController
   def autocomplete_sources
     note_type = params['type']
     note_id = params['type_id']
-    autocomplete = ::Projects::AutocompleteService.new(@project)
+    autocomplete = ::Projects::AutocompleteService.new(@project, current_user)
     participants = ::Projects::ParticipantsService.new(@project, current_user).execute(note_type, note_id)
 
     @suggestions = {
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index c72df73af46856e2f23184a602034ff403a611da..2daceed039b119a3a4b088e5cd256af1d696c43c 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -25,7 +25,7 @@ class SnippetsController < ApplicationController
         filter: :by_user,
         user: @user,
         scope: params[:scope] }).
-      page(params[:page]).per(PER_PAGE)
+      page(params[:page])
 
       render 'index'
     else
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index e10c633690f05dbc6ea3fc6aacbcf32675ebcc66..8e7956da48f132b7991bdd697ae96f896c0c8568 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -100,7 +100,7 @@ class UsersController < ApplicationController
   def load_projects
     @projects =
       PersonalProjectsFinder.new(@user).execute(current_user)
-      .page(params[:page]).per(PER_PAGE)
+      .page(params[:page])
   end
 
   def load_contributed_projects
@@ -108,7 +108,7 @@ class UsersController < ApplicationController
   end
 
   def load_groups
-    @groups = @user.groups.order_id_desc
+    @groups = JoinedGroupsFinder.new(@user).execute(current_user)
   end
 
   def projects_for_current_user
diff --git a/app/finders/contributed_projects_finder.rb b/app/finders/contributed_projects_finder.rb
index 0209649b017f672b4b5b65c68e637419dc1b379c..a685719555ceddfb4d06d984344ee4dbf7d24779 100644
--- a/app/finders/contributed_projects_finder.rb
+++ b/app/finders/contributed_projects_finder.rb
@@ -1,4 +1,4 @@
-class ContributedProjectsFinder
+class ContributedProjectsFinder < UnionFinder
   def initialize(user)
     @user = user
   end
@@ -11,27 +11,19 @@ class ContributedProjectsFinder
   #
   # Returns an ActiveRecord::Relation.
   def execute(current_user = nil)
-    if current_user
-      relation = projects_visible_to_user(current_user)
-    else
-      relation = public_projects
-    end
+    segments = all_projects(current_user)
 
-    relation.includes(:namespace).order_id_desc
+    find_union(segments, Project).includes(:namespace).order_id_desc
   end
 
   private
 
-  def projects_visible_to_user(current_user)
-    authorized = @user.contributed_projects.visible_to_user(current_user)
+  def all_projects(current_user)
+    projects = []
 
-    union = Gitlab::SQL::Union.
-      new([authorized.select(:id), public_projects.select(:id)])
+    projects << @user.contributed_projects.visible_to_user(current_user) if current_user
+    projects << @user.contributed_projects.public_to_user(current_user)
 
-    Project.where("projects.id IN (#{union.to_sql})")
-  end
-
-  def public_projects
-    @user.contributed_projects.public_only
+    projects
   end
 end
diff --git a/app/finders/group_projects_finder.rb b/app/finders/group_projects_finder.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3b9a421b11871ff7b174918cf812778a5f184524
--- /dev/null
+++ b/app/finders/group_projects_finder.rb
@@ -0,0 +1,42 @@
+class GroupProjectsFinder < UnionFinder
+  def initialize(group, options = {})
+    @group   = group
+    @options = options
+  end
+
+  def execute(current_user = nil)
+    segments = group_projects(current_user)
+    find_union(segments, Project)
+  end
+
+  private
+
+  def group_projects(current_user)
+    only_owned  = @options.fetch(:only_owned, false)
+    only_shared = @options.fetch(:only_shared, false)
+
+    projects = []
+
+    if current_user
+      if @group.users.include?(current_user)
+        projects << @group.projects unless only_shared
+        projects << @group.shared_projects unless only_owned
+      else
+        unless only_shared
+          projects << @group.projects.visible_to_user(current_user)
+          projects << @group.projects.public_to_user(current_user)
+        end
+
+        unless only_owned
+          projects << @group.shared_projects.visible_to_user(current_user)
+          projects << @group.shared_projects.public_to_user(current_user)
+        end
+      end
+    else
+      projects << @group.projects.public_only unless only_shared
+      projects << @group.shared_projects.public_only unless only_owned
+    end
+
+    projects
+  end
+end
diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4e43f42e9e18d51e355b5abe4c0891247f8f6740
--- /dev/null
+++ b/app/finders/groups_finder.rb
@@ -0,0 +1,18 @@
+class GroupsFinder < UnionFinder
+  def execute(current_user = nil)
+    segments = all_groups(current_user)
+
+    find_union(segments, Group).order_id_desc
+  end
+
+  private
+
+  def all_groups(current_user)
+    groups = []
+
+    groups << current_user.authorized_groups if current_user
+    groups << Group.unscoped.public_to_user(current_user)
+
+    groups
+  end
+end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 19e8c7a92be03a127619c98949cfc21283631201..046286dd9e1a895364c0d92fb960206831d0e46d 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -80,9 +80,10 @@ class IssuableFinder
       @projects = project
     elsif current_user && params[:authorized_only].presence && !current_user_related?
       @projects = current_user.authorized_projects.reorder(nil)
+    elsif group
+      @projects = GroupProjectsFinder.new(group).execute(current_user).reorder(nil)
     else
-      @projects = ProjectsFinder.new.execute(current_user, group: group).
-        reorder(nil)
+      @projects = ProjectsFinder.new.execute(current_user).reorder(nil)
     end
   end
 
@@ -171,14 +172,12 @@ class IssuableFinder
 
   def by_scope(items)
     case params[:scope]
-    when 'created-by-me', 'authored' then
+    when 'created-by-me', 'authored'
       items.where(author_id: current_user.id)
-    when 'all' then
-      items
-    when 'assigned-to-me' then
+    when 'assigned-to-me'
       items.where(assignee_id: current_user.id)
     else
-      raise 'You must specify default scope'
+      items
     end
   end
 
@@ -198,8 +197,7 @@ class IssuableFinder
   end
 
   def by_group(items)
-    items = items.of_group(group) if group
-
+    # Selection by group is already covered by `by_project` and `projects`
     items
   end
 
diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb
index 20a2b0ce8f0dbb21d25e8941954dd8b26b0128ca..c2befa5a5b3a4837010ecc1c035f36437c2f56d8 100644
--- a/app/finders/issues_finder.rb
+++ b/app/finders/issues_finder.rb
@@ -19,4 +19,10 @@ class IssuesFinder < IssuableFinder
   def klass
     Issue
   end
+
+  private
+
+  def init_collection
+    Issue.visible_to_user(current_user)
+  end
 end
diff --git a/app/finders/joined_groups_finder.rb b/app/finders/joined_groups_finder.rb
new file mode 100644
index 0000000000000000000000000000000000000000..471749802586feeb3918718f41e40eac0303874c
--- /dev/null
+++ b/app/finders/joined_groups_finder.rb
@@ -0,0 +1,24 @@
+class JoinedGroupsFinder < UnionFinder
+  def initialize(user)
+    @user = user
+  end
+
+  # Finds the groups of the source user, optionally limited to those visible to
+  # the current user.
+  def execute(current_user = nil)
+    segments = all_groups(current_user)
+
+    find_union(segments, Group).order_id_desc
+  end
+
+  private
+
+  def all_groups(current_user)
+    groups = []
+
+    groups << @user.authorized_groups.visible_to_user(current_user) if current_user
+    groups << @user.authorized_groups.public_to_user(current_user)
+
+    groups
+  end
+end
diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb
index a61ffa229900574be7d1e0c8304b4c29baa7d780..3ad4bd5f066fe38a5f5650ad48b04feff73ef146 100644
--- a/app/finders/personal_projects_finder.rb
+++ b/app/finders/personal_projects_finder.rb
@@ -1,4 +1,4 @@
-class PersonalProjectsFinder
+class PersonalProjectsFinder < UnionFinder
   def initialize(user)
     @user = user
   end
@@ -11,31 +11,19 @@ class PersonalProjectsFinder
   #
   # Returns an ActiveRecord::Relation.
   def execute(current_user = nil)
-    if current_user
-      relation = projects_visible_to_user(current_user)
-    else
-      relation = public_projects
-    end
+    segments = all_projects(current_user)
 
-    relation.includes(:namespace).order_id_desc
+    find_union(segments, Project).includes(:namespace).order_id_desc
   end
 
   private
 
-  def projects_visible_to_user(current_user)
-    authorized = @user.personal_projects.visible_to_user(current_user)
+  def all_projects(current_user)
+    projects = []
 
-    union = Gitlab::SQL::Union.
-      new([authorized.select(:id), public_and_internal_projects.select(:id)])
+    projects << @user.personal_projects.visible_to_user(current_user) if current_user
+    projects << @user.personal_projects.public_to_user(current_user)
 
-    Project.where("projects.id IN (#{union.to_sql})")
-  end
-
-  def public_projects
-    @user.personal_projects.public_only
-  end
-
-  def public_and_internal_projects
-    @user.personal_projects.public_and_internal_only
+    projects
   end
 end
diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb
index 3a5fc5b5907089e3ef6304bbcb8dba49e39b9863..2f0a9659d15b878a47c8555c6a0daca56d43bae2 100644
--- a/app/finders/projects_finder.rb
+++ b/app/finders/projects_finder.rb
@@ -1,81 +1,18 @@
-class ProjectsFinder
-  # Returns all projects, optionally including group projects a user has access
-  # to.
-  #
-  # ## Examples
-  #
-  # Retrieving all public projects:
-  #
-  #     ProjectsFinder.new.execute
-  #
-  # Retrieving all public/internal projects and those the given user has access
-  # to:
-  #
-  #     ProjectsFinder.new.execute(some_user)
-  #
-  # Retrieving all public/internal projects as well as the group's projects the
-  # user has access to:
-  #
-  #     ProjectsFinder.new.execute(some_user, group: some_group)
-  #
-  # Returns an ActiveRecord::Relation.
+class ProjectsFinder < UnionFinder
   def execute(current_user = nil, options = {})
-    group = options[:group]
+    segments = all_projects(current_user)
 
-    if group
-      segments = group_projects(current_user, group)
-    else
-      segments = all_projects(current_user)
-    end
-
-    if segments.length > 1
-      union = Gitlab::SQL::Union.new(segments.map { |s| s.select(:id) })
-
-      Project.where("projects.id IN (#{union.to_sql})")
-    else
-      segments.first
-    end
+    find_union(segments, Project)
   end
 
   private
 
-  def group_projects(current_user, group)
-    return [group.projects.public_only] unless current_user
-
-    user_group_projects = [
-       group_projects_for_user(current_user, group),
-       group.shared_projects.visible_to_user(current_user)
-    ]
-    if current_user.external?
-      user_group_projects << group.projects.public_only
-    else
-      user_group_projects << group.projects.public_and_internal_only
-    end
-  end
-
   def all_projects(current_user)
-    return [public_projects] unless current_user
+    projects = []
 
-    if current_user.external?
-      [current_user.authorized_projects, public_projects]
-    else
-      [current_user.authorized_projects, public_and_internal_projects]
-    end
-  end
-
-  def group_projects_for_user(current_user, group)
-    if group.users.include?(current_user)
-      group.projects
-    else
-      group.projects.visible_to_user(current_user)
-    end
-  end
-
-  def public_projects
-    Project.unscoped.public_only
-  end
+    projects << current_user.authorized_projects if current_user
+    projects << Project.unscoped.public_to_user(current_user)
 
-  def public_and_internal_projects
-    Project.unscoped.public_and_internal_only
+    projects
   end
 end
diff --git a/app/finders/union_finder.rb b/app/finders/union_finder.rb
new file mode 100644
index 0000000000000000000000000000000000000000..33cd1a491f3ca4297a9fa199185accb776c0349f
--- /dev/null
+++ b/app/finders/union_finder.rb
@@ -0,0 +1,11 @@
+class UnionFinder
+  def find_union(segments, klass)
+    if segments.length > 1
+      union = Gitlab::SQL::Union.new(segments.map { |s| s.select(:id) })
+
+      klass.where("#{klass.table_name}.id IN (#{union.to_sql})")
+    else
+      segments.first
+    end
+  end
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index d1b1c61b7106ee271a78a39753fd732b036baf7b..e6ceb21353209a7f44a5fbb3456b90399dba115f 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -182,7 +182,7 @@ module ApplicationHelper
   # Returns an HTML-safe String
   def time_ago_with_tooltip(time, placement: 'top', html_class: 'time_ago', skip_js: false)
     element = content_tag :time, time.to_s,
-      class: "#{html_class} js-timeago js-timeago-pending",
+      class: "#{html_class} js-timeago #{"js-timeago-pending" unless skip_js}",
       datetime: time.to_time.getutc.iso8601,
       title: time.in_time_zone.to_s(:medium),
       data: { toggle: 'tooltip', placement: placement, container: 'body' }
@@ -196,6 +196,22 @@ module ApplicationHelper
     element
   end
 
+  def edited_time_ago_with_tooltip(object, placement: 'top', html_class: 'time_ago', include_author: false)
+    return if object.updated_at == object.created_at
+
+    content_tag :small, class: "edited-text" do
+      output = content_tag(:span, "Edited ")
+      output << time_ago_with_tooltip(object.updated_at, placement: placement, html_class: html_class)
+
+      if include_author && object.updated_by && object.updated_by != object.author
+        output << content_tag(:span, " by ")
+        output << link_to_member(object.project, object.updated_by, avatar: false, author_class: nil)
+      end
+
+      output
+    end
+  end
+
   def render_markup(file_name, file_content)
     if gitlab_markdown?(file_name)
       Haml::Helpers.preserve(markdown(file_content))
@@ -285,7 +301,7 @@ module ApplicationHelper
       if project.nil?
         nil
       elsif current_controller?(:issues)
-        project.issues.send(entity).count
+        project.issues.visible_to_user(current_user).send(entity).count
       elsif current_controller?(:merge_requests)
         project.merge_requests.send(entity).count
       end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 0f77b3b299a11dd66dcd2e185a1bf792204fe721..820d69c230b9ed2aceb8893aa16ebc43947dc984 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -27,7 +27,7 @@ module BlobHelper
                                      link_opts)
 
     if !on_top_of_branch?(project, ref)
-      button_tag "Edit", class: "btn btn-default disabled has_tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' }
+      button_tag "Edit", class: "btn btn-default disabled has-tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' }
     elsif can_edit_blob?(blob, project, ref)
       link_to "Edit", edit_path, class: 'btn'
     elsif can?(current_user, :fork_project, project)
@@ -50,9 +50,9 @@ module BlobHelper
     return unless blob
 
     if !on_top_of_branch?(project, ref)
-      button_tag label, class: "btn btn-#{btn_class} disabled has_tooltip", title: "You can only #{action} files when you are on a branch", data: { container: 'body' }
+      button_tag label, class: "btn btn-#{btn_class} disabled has-tooltip", title: "You can only #{action} files when you are on a branch", data: { container: 'body' }
     elsif blob.lfs_pointer?
-      button_tag label, class: "btn btn-#{btn_class} disabled has_tooltip", title: "It is not possible to #{action} files that are stored in LFS using the web interface", data: { container: 'body' }
+      button_tag label, class: "btn btn-#{btn_class} disabled has-tooltip", title: "It is not possible to #{action} files that are stored in LFS using the web interface", data: { container: 'body' }
     elsif can_edit_blob?(blob, project, ref)
       button_tag label, class: "btn btn-#{btn_class}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal'
     elsif can?(current_user, :fork_project, project)
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index d6c0584374358f6bda94b560208126dec523b0cf..a9047ede8c58302432dbfc82b504adc1ece5011a 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -23,36 +23,34 @@ module ButtonHelper
   end
 
   def http_clone_button(project)
-    klass = 'btn js-protocol-switch'
-    klass << ' active'      if default_clone_protocol == 'http'
-    klass << ' has_tooltip' if current_user.try(:require_password?)
+    klass = 'http-selector'
+    klass << ' has-tooltip' if current_user.try(:require_password?)
 
     protocol = gitlab_config.protocol.upcase
 
-    content_tag :button, protocol,
+    content_tag :a, protocol,
       class: klass,
+      href: @project.http_url_to_repo,
       data: {
-        clone: project.http_url_to_repo,
+        html: true,
+        placement: 'right',
         container: 'body',
-        html: 'true',
         title: "Set a password on your account<br>to pull or push via #{protocol}"
-      },
-      type: :button
+      }
   end
 
   def ssh_clone_button(project)
-    klass = 'btn js-protocol-switch'
-    klass << ' active'      if default_clone_protocol == 'ssh'
-    klass << ' has_tooltip' if current_user.try(:require_ssh_key?)
+    klass = 'ssh-selector'
+    klass << ' has-tooltip' if current_user.try(:require_ssh_key?)
 
-    content_tag :button, 'SSH',
+    content_tag :a, 'SSH',
       class: klass,
+      href: project.ssh_url_to_repo,
       data: {
-        clone: project.ssh_url_to_repo,
+        html: true,
+        placement: 'right',
         container: 'body',
-        html: 'true',
         title: 'Add an SSH key to your profile<br>to pull or push via SSH.'
-      },
-      type: :button
+      }
   end
 end
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index f994c9e61707032023e15abb407c168d5695c457..bde0799f3dea5a94b111ed95cbc56074bf9ed8e4 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -182,7 +182,7 @@ module CommitsHelper
       end
 
     options = {
-      class: "commit-#{options[:source]}-link has_tooltip",
+      class: "commit-#{options[:source]}-link has-tooltip",
       data: { 'original-title'.to_sym => sanitize(source_email) }
     }
 
diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb
index 74f326e0b83815ebf8f491fe462ff2468b59e863..ceff1fbb1618a0d16b1f658a438eabc4e0fc4da5 100644
--- a/app/helpers/dropdowns_helper.rb
+++ b/app/helpers/dropdowns_helper.rb
@@ -24,7 +24,7 @@ module DropdownsHelper
           capture(&block) if block && !options.has_key?(:footer_content)
         end
 
-        if block && options.has_key?(:footer_content)
+        if block && options[:footer_content]
           output << content_tag(:div, class: "dropdown-footer") do
             capture(&block)
           end
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index 37a888d9c60bdd3d70ffa1c202e2ae3c53a0f77e..a67a6b208e256bdfb9aa4023569e57b16b071ed4 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -194,7 +194,7 @@ module EventsHelper
   end
 
   def event_to_atom(xml, event)
-    if event.proper?
+    if event.proper?(current_user)
       xml.entry do
         event_link = event_feed_url(event)
         event_title = event_feed_title(event)
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 1d36969cd62c49a061c65341a0084b9f6f3a12c7..b1f0a765bb90655bc41fa416ee2bf48b34216432 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -19,6 +19,10 @@ module GroupsHelper
     end
   end
 
+  def can_change_group_visibility_level?(group)
+    can?(current_user, :change_visibility_level, group)
+  end
+
   def group_icon(group)
     if group.is_a?(String)
       group = Group.find_by(path: group)
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 2dfeddf7368684c23b47e716d1ee5abd872d638b..81df2094392dbfa79e966bf45137ec57330c251a 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -20,6 +20,23 @@ module IssuablesHelper
     base_issuable_scope(issuable).where('iid < ?', issuable.iid).first
   end
 
+  def user_dropdown_label(user_id, default_label)
+    return "Unassigned" if user_id == "0"
+
+    if @project
+      member = @project.team.find_member(user_id)
+      user = member.user if member
+    else
+      user = User.find_by(id: user_id)
+    end
+
+    if user
+      user.name
+    else
+      default_label
+    end
+  end
+
   private
 
   def sidebar_gutter_collapsed?
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index ae4ebc0854a9e03aa7999f5cbe982177e536a960..24b90fef4fe94169f984f1450e2fa4195ed17121 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -57,6 +57,19 @@ module IssuesHelper
     options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id)
   end
 
+  def project_options(issuable, current_user, ability: :read_project)
+    projects = current_user.authorized_projects
+    projects = projects.select do |project|
+      current_user.can?(ability, project)
+    end
+
+    no_project = OpenStruct.new(id: 0, name_with_namespace: 'No project')
+    projects.unshift(no_project)
+    projects.delete(issuable.project)
+
+    options_from_collection_for_select(projects, :id, :name_with_namespace)
+  end
+
   def status_box_class(item)
     if item.respond_to?(:expired?) && item.expired?
       'status-box-expired'
@@ -98,6 +111,10 @@ module IssuesHelper
     end.sort.to_sentence(last_word_connector: ', or ')
   end
 
+  def confidential_icon(issue)
+    icon('eye-slash') if issue.confidential?
+  end
+
   def emoji_icon(name, unicode = nil, aliases = [])
     unicode ||= Emoji.emoji_filename(name) rescue ""
 
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index 4455dcd0e20e166af5b62321737dfd4ab9d22c12..e0a8552dfa757c37c10d1c150b57855d5986f3fc 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -32,7 +32,7 @@ module LabelsHelper
   #   link_to_label(label) { "My Custom Label Text" }
   #
   # Returns a String
-  def link_to_label(label, project: nil, type: :issue, &block)
+  def link_to_label(label, project: nil, type: :issue, tooltip: true, &block)
     project ||= @project || label.project
     link = send("namespace_project_#{type.to_s.pluralize}_path",
                 project.namespace,
@@ -42,7 +42,7 @@ module LabelsHelper
     if block_given?
       link_to link, &block
     else
-      link_to render_colored_label(label), link
+      link_to render_colored_label(label, tooltip: tooltip), link
     end
   end
 
@@ -50,23 +50,24 @@ module LabelsHelper
     @project.labels.pluck(:title)
   end
 
-  def render_colored_label(label, label_suffix = '')
+  def render_colored_label(label, label_suffix = '', tooltip: true)
     label_color = label.color || Label::DEFAULT_COLOR
     text_color = text_color_for_bg(label_color)
 
     # Intentionally not using content_tag here so that this method can be called
     # by LabelReferenceFilter
-    span = %(<span class="label color-label") +
-      %(style="background-color: #{label_color}; color: #{text_color}">) +
+    span = %(<span class="label color-label #{"has-tooltip" if tooltip}" ) +
+      %(style="background-color: #{label_color}; color: #{text_color}" ) +
+      %(title="#{escape_once(label.description)}" data-container="body">) +
       %(#{escape_once(label.name)}#{label_suffix}</span>)
 
     span.html_safe
   end
 
-  def render_colored_cross_project_label(label)
+  def render_colored_cross_project_label(label, tooltip: true)
     label_suffix = label.project.name_with_namespace
     label_suffix = " <i>in #{escape_once(label_suffix)}</i>"
-    render_colored_label(label, label_suffix)
+    render_colored_label(label, label_suffix, tooltip: tooltip)
   end
 
   def suggested_colors
@@ -109,19 +110,12 @@ module LabelsHelper
     end
   end
 
-  def projects_labels_options
-    labels =
-      if @project
-        @project.labels
-      else
-        Label.where(project_id: @projects)
-      end
-
-    grouped_labels = GlobalLabel.build_collection(labels)
-    grouped_labels.unshift(Label::None)
-    grouped_labels.unshift(Label::Any)
-
-    options_from_collection_for_select(grouped_labels, 'name', 'title', params[:label_name])
+  def labels_filter_path
+    if @project
+      namespace_project_labels_path(@project.namespace, @project, :json)
+    else
+      labels_dashboard_path(:json)
+    end
   end
 
   def label_subscription_status(label)
diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb
index e8ac8788d9dcc3af98c0d8f257fbf426847197dd..c9d8787bd196c0abc5c41475767bf6ff65468760 100644
--- a/app/helpers/milestones_helper.rb
+++ b/app/helpers/milestones_helper.rb
@@ -38,7 +38,7 @@ module MilestonesHelper
   def milestone_progress_bar(milestone)
     options = {
       class: 'progress-bar progress-bar-success',
-      style: "width: #{milestone.percent_complete}%;"
+      style: "width: #{milestone.percent_complete(current_user)}%;"
     }
 
     content_tag :div, class: 'progress' do
@@ -46,22 +46,12 @@ module MilestonesHelper
     end
   end
 
-  def projects_milestones_options
-    milestones =
-      if @project
-        @project.milestones
-      else
-        Milestone.where(project_id: @projects)
-      end.active
-
-    epoch = DateTime.parse('1970-01-01')
-    grouped_milestones = GlobalMilestone.build_collection(milestones)
-    grouped_milestones = grouped_milestones.sort_by { |x| x.due_date.nil? ? epoch : x.due_date }
-    grouped_milestones.unshift(Milestone::None)
-    grouped_milestones.unshift(Milestone::Any)
-    grouped_milestones.unshift(Milestone::Upcoming)
-
-    options_from_collection_for_select(grouped_milestones, 'name', 'title', params[:milestone_title])
+  def milestones_filter_dropdown_path
+    if @project
+      namespace_project_milestones_path(@project.namespace, @project, :json)
+    else
+      milestones_dashboard_path(:json)
+    end
   end
 
   def milestone_remaining_days(milestone)
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index b5acb80b72092194cdc883aba2878f6984f88fa6..4e4c6e301d509e8c975698e545c0b07980a71f77 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -26,7 +26,7 @@ module ProjectsHelper
     image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar]
   end
 
-  def link_to_member(project, author, opts = {})
+  def link_to_member(project, author, opts = {}, &block)
     default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" }
     opts = default_opts.merge(opts)
 
@@ -44,13 +44,15 @@ module ProjectsHelper
       author_html << content_tag(:span, sanitize(author.name), class: opts[:author_class]) if opts[:name]
     end
 
+    author_html << capture(&block) if block
+
     author_html = author_html.html_safe
 
     if opts[:name]
       link_to(author_html, user_path(author), class: "author_link #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe
     else
       title = opts[:title].sub(":name", sanitize(author.name))
-      link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { 'original-title'.to_sym => title, container: 'body' } ).html_safe
+      link_to(author_html, user_path(author), class: "author_link has-tooltip", data: { 'original-title'.to_sym => title, container: 'body' } ).html_safe
     end
   end
 
@@ -207,7 +209,7 @@ module ProjectsHelper
 
   def default_clone_protocol
     if !current_user || current_user.require_ssh_key?
-      "http"
+      gitlab_config.protocol
     else
       "ssh"
     end
diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb
index 07ddc691d859845371d865f73f99e39e69b20eb2..edc5686cf08e68e294d78deb58e9bee0f986dffd 100644
--- a/app/helpers/todos_helper.rb
+++ b/app/helpers/todos_helper.rb
@@ -16,14 +16,19 @@ module TodosHelper
 
   def todo_target_link(todo)
     target = todo.target_type.titleize.downcase
-    link_to "#{target} #{todo.target.to_reference}", todo_target_path(todo), { title: h(todo.target.title) }
+    link_to "#{target} #{todo.target_reference}", todo_target_path(todo), { title: todo.target.title }
   end
 
   def todo_target_path(todo)
     anchor = dom_id(todo.note) if todo.note.present?
 
-    polymorphic_path([todo.project.namespace.becomes(Namespace),
-                      todo.project, todo.target], anchor: anchor)
+    if todo.for_commit?
+      namespace_project_commit_path(todo.project.namespace.becomes(Namespace), todo.project,
+                                    todo.target, anchor: anchor)
+    else
+      polymorphic_path([todo.project.namespace.becomes(Namespace),
+                        todo.project, todo.target], anchor: anchor)
+    end
   end
 
   def todos_filter_params
diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb
index 71d33b445c22bea272c664e004ef0f01401ea4db..3a83ae15dd8627951a718f8f182147406e64b4d9 100644
--- a/app/helpers/visibility_level_helper.rb
+++ b/app/helpers/visibility_level_helper.rb
@@ -19,6 +19,8 @@ module VisibilityLevelHelper
     case form_model
     when Project
       project_visibility_level_description(level)
+    when Group
+      group_visibility_level_description(level)
     when Snippet
       snippet_visibility_level_description(level, form_model)
     end
@@ -35,6 +37,17 @@ module VisibilityLevelHelper
     end
   end
 
+  def group_visibility_level_description(level)
+    case level
+    when Gitlab::VisibilityLevel::PRIVATE
+      "The group and its projects can only be viewed by members."
+    when Gitlab::VisibilityLevel::INTERNAL
+      "The group and any internal projects can be viewed by any logged in user."
+    when Gitlab::VisibilityLevel::PUBLIC
+      "The group and any public projects can be viewed without any authentication."
+    end
+  end
+
   def snippet_visibility_level_description(level, snippet = nil)
     case level
     when Gitlab::VisibilityLevel::PRIVATE
@@ -50,6 +63,23 @@ module VisibilityLevelHelper
     end
   end
 
+  def visibility_icon_description(form_model)
+    case form_model
+    when Project
+      project_visibility_icon_description(form_model.visibility_level)
+    when Group
+      group_visibility_icon_description(form_model.visibility_level)
+    end
+  end
+
+  def group_visibility_icon_description(level)
+    "#{visibility_level_label(level)} - #{group_visibility_level_description(level)}"
+  end
+
+  def project_visibility_icon_description(level)
+    "#{visibility_level_label(level)} - #{project_visibility_level_description(level)}"
+  end
+
   def visibility_level_label(level)
     Project.visibility_levels.key(level)
   end
@@ -67,8 +97,11 @@ module VisibilityLevelHelper
     current_application_settings.default_snippet_visibility
   end
 
+  def default_group_visibility
+    current_application_settings.default_group_visibility
+  end
+
   def skip_level?(form_model, level)
-    form_model.is_a?(Project) &&
-    !form_model.visibility_level_allowed?(level)
+    form_model.is_a?(Project) && !form_model.visibility_level_allowed?(level)
   end
 end
diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb
index 5f9adb32e0094faae023f1dff14dfcc9a9fc4176..6f54c42146c6d09f961ebf904adc1b89077bc596 100644
--- a/app/mailers/emails/issues.rb
+++ b/app/mailers/emails/issues.rb
@@ -36,6 +36,14 @@ module Emails
       mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
     end
 
+    def issue_moved_email(recipient, issue, new_issue, updated_by_user)
+      setup_issue_mail(issue.id, recipient.id)
+
+      @new_issue = new_issue
+      @new_project = new_issue.project
+      mail_answer_thread(issue, issue_thread_options(updated_by_user.id, recipient.id))
+    end
+
     private
 
     def setup_issue_mail(issue_id, recipient_id)
diff --git a/app/models/ability.rb b/app/models/ability.rb
index ccac08b7d3f1542144b1d73b97dd34d3916f4a32..fa2345f6faa3355c80220999f6e879e6fff8054c 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -49,7 +49,6 @@ class Ability
         rules = [
           :read_project,
           :read_wiki,
-          :read_issue,
           :read_label,
           :read_milestone,
           :read_project_snippet,
@@ -63,6 +62,9 @@ class Ability
         # Allow to read builds by anonymous user if guests are allowed
         rules << :read_build if project.public_builds?
 
+        # Allow to read issues by anonymous user if issue is not confidential
+        rules << :read_issue unless subject.is_a?(Issue) && subject.confidential?
+
         rules - project_disabled_features_rules(project)
       else
         []
@@ -83,7 +85,7 @@ class Ability
                 subject.group
               end
 
-      if group && group.projects.public_only.any?
+      if group && group.public?
         [:read_group]
       else
         []
@@ -112,6 +114,13 @@ class Ability
         # Push abilities on the users team role
         rules.push(*project_team_rules(project.team, user))
 
+        if project.owner == user ||
+          (project.group && project.group.has_owner?(user)) ||
+          user.admin?
+
+          rules.push(*project_owner_rules)
+        end
+
         if project.public? || (project.internal? && !user.external?)
           rules.push(*public_project_rules)
 
@@ -119,14 +128,6 @@ class Ability
           rules << :read_build if project.public_builds?
         end
 
-        if project.owner == user || user.admin?
-          rules.push(*project_admin_rules)
-        end
-
-        if project.group && project.group.has_owner?(user)
-          rules.push(*project_admin_rules)
-        end
-
         if project.archived?
           rules -= project_archived_rules
         end
@@ -169,7 +170,8 @@ class Ability
         :read_note,
         :create_project,
         :create_issue,
-        :create_note
+        :create_note,
+        :upload_file
       ]
     end
 
@@ -226,14 +228,16 @@ class Ability
       ]
     end
 
-    def project_admin_rules
-      @project_admin_rules ||= project_master_rules + [
+    def project_owner_rules
+      @project_owner_rules ||= project_master_rules + [
         :change_namespace,
         :change_visibility_level,
         :rename_project,
         :remove_project,
         :archive_project,
-        :remove_fork_project
+        :remove_fork_project,
+        :destroy_merge_request,
+        :destroy_issue
       ]
     end
 
@@ -271,11 +275,9 @@ class Ability
     def group_abilities(user, group)
       rules = []
 
-      if user.admin? || group.users.include?(user) || ProjectsFinder.new.execute(user, group: group).any?
-        rules << :read_group
-      end
+      rules << :read_group if can_read_group?(user, group)
 
-      # Only group masters and group owners can create new projects in group
+      # Only group masters and group owners can create new projects
       if group.has_master?(user) || group.has_owner?(user) || user.admin?
         rules += [
           :create_projects,
@@ -288,13 +290,23 @@ class Ability
         rules += [
           :admin_group,
           :admin_namespace,
-          :admin_group_member
+          :admin_group_member,
+          :change_visibility_level
         ]
       end
 
       rules.flatten
     end
 
+    def can_read_group?(user, group)
+      return true if user.admin?
+      return true if group.public?
+      return true if group.internal? && !user.external?
+      return true if group.users.include?(user)
+
+      GroupProjectsFinder.new(group).execute(user).any?
+    end
+
     def namespace_abilities(user, namespace)
       rules = []
 
@@ -321,6 +333,7 @@ class Ability
         end
 
         rules += project_abilities(user, subject.project)
+        rules = filter_confidential_issues_abilities(user, subject, rules) if subject.is_a?(Issue)
         rules
       end
     end
@@ -439,5 +452,17 @@ class Ability
         :"admin_#{name}"
       ]
     end
+
+    def filter_confidential_issues_abilities(user, issue, rules)
+      return rules if user.admin? || !issue.confidential?
+
+      unless issue.author == user || issue.assignee == user || issue.project.team.member?(user.id)
+        rules.delete(:admin_issue)
+        rules.delete(:read_issue)
+        rules.delete(:update_issue)
+      end
+
+      rules
+    end
   end
 end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 269056e0e7739a408ce7637861113b8286428d37..c4879598c4eaefca67616c7afe26fc11dc31840f 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -18,6 +18,7 @@
 #  max_attachment_size               :integer          default(10), not null
 #  default_project_visibility        :integer
 #  default_snippet_visibility        :integer
+#  default_group_visibility          :integer
 #  restricted_signup_domains         :text
 #  user_oauth_applications           :boolean          default(TRUE)
 #  after_sign_out_path               :string(255)
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 3b1aa0f5c80150dbd4c3d443014fc57d6d73582d..3377a85a55a7a103a451d44baf6060398d76223b 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -114,7 +114,7 @@ class CommitStatus < ActiveRecord::Base
   end
 
   def ignored?
-    failed? && allow_failure?
+    allow_failure? && (failed? || canceled?)
   end
 
   def duration
diff --git a/app/models/concerns/internal_id.rb b/app/models/concerns/internal_id.rb
index 821ed54fb987568a3fd41c638709127e1fefccb1..51288094ef1fa02ac083e5e6e6fefced1f2bab73 100644
--- a/app/models/concerns/internal_id.rb
+++ b/app/models/concerns/internal_id.rb
@@ -7,7 +7,10 @@ module InternalId
   end
 
   def set_iid
-    max_iid = project.send(self.class.name.tableize).maximum(:iid)
+    records = project.send(self.class.name.tableize)
+    records = records.with_deleted if self.paranoid?
+    max_iid = records.maximum(:iid)
+
     self.iid = max_iid.to_i + 1
   end
 
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 86ab84615ba8f29d09c77e12ba0ad9f167bea4a2..476e1ce7af0bc3141fe5fb1bf0556cbce340d5ff 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -58,6 +58,8 @@ module Issuable
     attr_mentionable :description, cache: true
     participant :author, :assignee, :notes_with_associations
     strip_attributes :title
+
+    acts_as_paranoid
   end
 
   module ClassMethods
@@ -209,4 +211,13 @@ module Issuable
     Taskable.get_updated_tasks(old_content: previous_changes['description'].first,
                                new_content: description)
   end
+
+  ##
+  # Method that checks if issuable can be moved to another project.
+  #
+  # Should be overridden if issuable can be moved.
+  #
+  def can_move?(*)
+    false
+  end
 end
diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb
index d67df7c1d9c1933592f0ceebb40ebdd6f0af3b61..5b8e3f654ea6be9a667ad6a08557ef0bb245a789 100644
--- a/app/models/concerns/milestoneish.rb
+++ b/app/models/concerns/milestoneish.rb
@@ -1,18 +1,18 @@
 module Milestoneish
-  def closed_items_count
-    issues.closed.size + merge_requests.closed_and_merged.size
+  def closed_items_count(user = nil)
+    issues_visible_to_user(user).closed.size + merge_requests.closed_and_merged.size
   end
 
-  def total_items_count
-    issues.size + merge_requests.size
+  def total_items_count(user = nil)
+    issues_visible_to_user(user).size + merge_requests.size
   end
 
-  def complete?
-    total_items_count == closed_items_count
+  def complete?(user = nil)
+    total_items_count(user) == closed_items_count(user)
   end
 
-  def percent_complete
-    ((closed_items_count * 100) / total_items_count).abs
+  def percent_complete(user = nil)
+    ((closed_items_count(user) * 100) / total_items_count(user)).abs
   rescue ZeroDivisionError
     0
   end
@@ -22,4 +22,8 @@ module Milestoneish
 
     (due_date - Date.today).to_i
   end
+
+  def issues_visible_to_user(user = nil)
+    issues.visible_to_user(user)
+  end
 end
diff --git a/app/models/event.rb b/app/models/event.rb
index 9a0bbf50f8b529753256e3b0755af46acf9cc477..a5cfeaf388ea4563d4df915a35c03a1a8bec891a 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -73,15 +73,17 @@ class Event < ActiveRecord::Base
     end
   end
 
-  def proper?
+  def proper?(user = nil)
     if push?
       true
     elsif membership_changed?
       true
     elsif created_project?
       true
+    elsif issue?
+      Ability.abilities.allowed?(user, :read_issue, issue)
     else
-      ((issue? || merge_request? || note?) && target) || milestone?
+      ((merge_request? || note?) && target) || milestone?
     end
   end
 
diff --git a/app/models/group.rb b/app/models/group.rb
index 9919ca112dc0530ee72cea0bdd9edcb15a379260..b332601c59b78bc70e1904aa644fb626034c05f0 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -2,15 +2,16 @@
 #
 # Table name: namespaces
 #
-#  id          :integer          not null, primary key
-#  name        :string(255)      not null
-#  path        :string(255)      not null
-#  owner_id    :integer
-#  created_at  :datetime
-#  updated_at  :datetime
-#  type        :string(255)
-#  description :string(255)      default(""), not null
-#  avatar      :string(255)
+#  id                     :integer          not null, primary key
+#  name                   :string(255)      not null
+#  path                   :string(255)      not null
+#  owner_id               :integer
+#  visibility_level       :integer          default(20), not null
+#  created_at             :datetime
+#  updated_at             :datetime
+#  type                   :string(255)
+#  description            :string(255)      default(""), not null
+#  avatar                 :string(255)
 #
 
 require 'carrierwave/orm/activerecord'
@@ -18,6 +19,7 @@ require 'file_size_validator'
 
 class Group < Namespace
   include Gitlab::ConfigHelper
+  include Gitlab::VisibilityLevel
   include Referable
 
   has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember'
@@ -27,6 +29,8 @@ class Group < Namespace
   has_many :shared_projects, through: :project_group_links, source: :project
 
   validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
+  validate :visibility_level_allowed_by_projects
+
   validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
 
   mount_uploader :avatar, AvatarUploader
@@ -74,6 +78,21 @@ class Group < Namespace
     name
   end
 
+  def visibility_level_field
+    visibility_level
+  end
+
+  def visibility_level_allowed_by_projects
+    allowed_by_projects = self.projects.where('visibility_level > ?', self.visibility_level).none?
+
+    unless allowed_by_projects
+      level_name = Gitlab::VisibilityLevel.level_name(visibility_level).downcase
+      self.errors.add(:visibility_level, "#{level_name} is not allowed since there are projects with higher visibility.")
+    end
+
+    allowed_by_projects
+  end
+
   def avatar_url(size = nil)
     if avatar.present?
       [gitlab_config.url, avatar.url].join
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 2447f860c5ad015e630be696549af33b8d0756b5..f32db59ac9fd40e68c49286e5833aa4fcb1db4e2 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -16,6 +16,7 @@
 #  state         :string(255)
 #  iid           :integer
 #  updated_by_id :integer
+#  moved_to_id   :integer
 #
 
 require 'carrierwave/orm/activerecord'
@@ -31,10 +32,9 @@ class Issue < ActiveRecord::Base
   ActsAsTaggableOn.strict_case_match = true
 
   belongs_to :project
-  validates :project, presence: true
+  belongs_to :moved_to, class_name: 'Issue'
 
-  scope :of_group,
-    ->(group) { where(project_id: group.projects.select(:id).reorder(nil)) }
+  validates :project, presence: true
 
   scope :cared, ->(user) { where(assignee_id: user) }
   scope :open_for, ->(user) { opened.assigned_to(user) }
@@ -58,6 +58,13 @@ class Issue < ActiveRecord::Base
     attributes
   end
 
+  def self.visible_to_user(user)
+    return where(confidential: false) if user.blank?
+    return all if user.admin?
+
+    where('issues.confidential = false OR (issues.confidential = true AND (issues.author_id = :user_id OR issues.assignee_id = :user_id OR issues.project_id IN(:project_ids)))', user_id: user.id, project_ids: user.authorized_projects.select(:id))
+  end
+
   def self.reference_prefix
     '#'
   end
@@ -98,9 +105,8 @@ class Issue < ActiveRecord::Base
   end
 
   def related_branches
-    return [] if self.project.empty_repo?
-    self.project.repository.branch_names.select do |branch|
-      branch =~ /\A#{iid}-(?!\d+-stable)/i
+    project.repository.branch_names.select do |branch|
+      branch.end_with?("-#{iid}")
     end
   end
 
@@ -131,8 +137,20 @@ class Issue < ActiveRecord::Base
     end.uniq.select { |mr| mr.open? && mr.closes_issue?(self) }
   end
 
+  def moved?
+    !moved_to.nil?
+  end
+
+  def can_move?(user, to_project = nil)
+    if to_project
+      return false unless user.can?(:admin_issue, to_project)
+    end
+
+    !moved? && user.can?(:admin_issue, self.project)
+  end
+
   def to_branch_name
-    "#{iid}-#{title.parameterize}"
+    "#{title.parameterize}-#{iid}"
   end
 
   def can_be_worked_on?(current_user)
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 188325045e2f80968ea0e48896755ec701c1f1d7..ef48207f9568595c57963ecbb774636c6cc13c33 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -131,7 +131,6 @@ class MergeRequest < ActiveRecord::Base
   validate :validate_branches
   validate :validate_fork
 
-  scope :of_group, ->(group) { where("source_project_id in (:group_project_ids) OR target_project_id in (:group_project_ids)", group_project_ids: group.projects.select(:id).reorder(nil)) }
   scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) }
   scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) }
   scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
@@ -277,8 +276,14 @@ class MergeRequest < ActiveRecord::Base
     self.target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last
   end
 
+  WIP_REGEX = /\A\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i.freeze
+
   def work_in_progress?
-    !!(title =~ /\A\[?WIP(\]|:| )/i)
+    title =~ WIP_REGEX
+  end
+
+  def wipless_title
+    self.title.sub(WIP_REGEX, "")
   end
 
   def mergeable?
@@ -516,11 +521,15 @@ class MergeRequest < ActiveRecord::Base
   end
 
   def target_sha
-    @target_sha ||= target_project.repository.commit(target_branch).sha
+    @target_sha ||= target_project.repository.commit(target_branch).try(:sha)
   end
 
   def source_sha
-    last_commit.try(:sha)
+    last_commit.try(:sha) || source_tip.try(:sha)
+  end
+
+  def source_tip
+    source_branch && source_project.repository.commit(source_branch)
   end
 
   def fetch_ref
@@ -568,8 +577,11 @@ class MergeRequest < ActiveRecord::Base
   end
 
   def compute_diverged_commits_count
+    return 0 unless source_sha && target_sha
+
     Gitlab::Git::Commit.between(target_project.repository.raw_repository, source_sha, target_sha).size
   end
+  private :compute_diverged_commits_count
 
   def diverged_from_target_branch?
     diverged_commits_count > 0
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index 374590ba0c51093c1990978062c9c6b20b4e0f76..de7183bf6b4e836b97700f6c816998c1afe5d771 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -121,8 +121,8 @@ class Milestone < ActiveRecord::Base
     active? && issues.opened.count.zero?
   end
 
-  def is_empty?
-    total_items_count.zero?
+  def is_empty?(user = nil)
+    total_items_count(user).zero?
   end
 
   def author_id
diff --git a/app/models/project.rb b/app/models/project.rb
index ab4913e99a8530425886ebdec3bd74b62ecc279f..9c8246e8ac0c8a757e04edb14d1193bf00d9343d 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -73,7 +73,7 @@ class Project < ActiveRecord::Base
     update_column(:last_activity_at, self.created_at)
   end
 
-  # update visibility_levet of forks
+  # update visibility_level of forks
   after_update :update_forks_visibility_level
   def update_forks_visibility_level
     return unless visibility_level < visibility_level_was
@@ -197,6 +197,8 @@ class Project < ActiveRecord::Base
   validate :avatar_type,
     if: ->(project) { project.avatar.present? && project.avatar_changed? }
   validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
+  validate :visibility_level_allowed_by_group
+  validate :visibility_level_allowed_as_fork
 
   add_authentication_token_field :runners_token
   before_save :ensure_runners_token
@@ -215,8 +217,6 @@ class Project < ActiveRecord::Base
   scope :in_group_namespace, -> { joins(:group) }
   scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
   scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) }
-  scope :public_only, -> { where(visibility_level: Project::PUBLIC) }
-  scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) }
   scope :non_archived, -> { where(archived: false) }
   scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct }
 
@@ -246,10 +246,6 @@ class Project < ActiveRecord::Base
   end
 
   class << self
-    def public_and_internal_levels
-      [Project::PUBLIC, Project::INTERNAL]
-    end
-
     def abandoned
       where('projects.last_activity_at < ?', 6.months.ago)
     end
@@ -435,6 +431,7 @@ class Project < ActiveRecord::Base
   def safe_import_url
     result = URI.parse(self.import_url)
     result.password = '*****' unless result.password.nil?
+    result.user = '*****' unless result.user.nil? || result.user == "git" #tokens or other data may be saved as user
     result.to_s
   rescue
     self.import_url
@@ -442,10 +439,25 @@ class Project < ActiveRecord::Base
 
   def check_limit
     unless creator.can_create_project? or namespace.kind == 'group'
-      errors[:limit_reached] << ("Your project limit is #{creator.projects_limit} projects! Please contact your administrator to increase it")
+      self.errors.add(:limit_reached, "Your project limit is #{creator.projects_limit} projects! Please contact your administrator to increase it")
     end
   rescue
-    errors[:base] << ("Can't check your ability to create project")
+    self.errors.add(:base, "Can't check your ability to create project")
+  end
+
+  def visibility_level_allowed_by_group
+    return if visibility_level_allowed_by_group?
+
+    level_name = Gitlab::VisibilityLevel.level_name(self.visibility_level).downcase
+    group_level_name = Gitlab::VisibilityLevel.level_name(self.group.visibility_level).downcase
+    self.errors.add(:visibility_level, "#{level_name} is not allowed in a #{group_level_name} group.")
+  end
+
+  def visibility_level_allowed_as_fork
+    return if visibility_level_allowed_as_fork?
+
+    level_name = Gitlab::VisibilityLevel.level_name(self.visibility_level).downcase
+    self.errors.add(:visibility_level, "#{level_name} is not allowed since the fork source project has lower visibility.")
   end
 
   def to_param
@@ -571,10 +583,7 @@ class Project < ActiveRecord::Base
   end
 
   def avatar_in_git
-    @avatar_file ||= 'logo.png' if repository.blob_at_branch('master', 'logo.png')
-    @avatar_file ||= 'logo.jpg' if repository.blob_at_branch('master', 'logo.jpg')
-    @avatar_file ||= 'logo.gif' if repository.blob_at_branch('master', 'logo.gif')
-    @avatar_file
+    repository.avatar
   end
 
   def avatar_url
@@ -879,6 +888,7 @@ class Project < ActiveRecord::Base
     # Forked import is handled asynchronously
     unless forked?
       if gitlab_shell.add_repository(path_with_namespace)
+        repository.after_create
         true
       else
         errors.add(:base, 'Failed to create repository via gitlab-shell')
@@ -963,9 +973,25 @@ class Project < ActiveRecord::Base
     issues.opened.count
   end
 
-  def visibility_level_allowed?(level)
+  def visibility_level_allowed_as_fork?(level = self.visibility_level)
     return true unless forked?
-    Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level.to_i)
+
+    # self.forked_from_project will be nil before the project is saved, so
+    # we need to go through the relation
+    original_project = forked_project_link.forked_from_project
+    return true unless original_project
+
+    level <= original_project.visibility_level
+  end
+
+  def visibility_level_allowed_by_group?(level = self.visibility_level)
+    return true unless group
+
+    level <= group.visibility_level
+  end
+
+  def visibility_level_allowed?(level = self.visibility_level)
+    visibility_level_allowed_as_fork?(level) && visibility_level_allowed_by_group?(level)
   end
 
   def runners_token
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index 59b1b86d1fb5c9b9225d3d00c7b146bc0468100f..7c1a61bb0bfcb99a8df8ae2751b7aced85da5f11 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -123,23 +123,27 @@ class ProjectWiki
   end
 
   def repository
-    Repository.new(path_with_namespace, @project)
+    @repository ||= Repository.new(path_with_namespace, @project)
   end
 
   def default_branch
     wiki.class.default_ref
   end
 
-  private
-
   def create_repo!
     if init_repo(path_with_namespace)
-      Gollum::Wiki.new(path_to_repo)
+      wiki = Gollum::Wiki.new(path_to_repo)
     else
       raise CouldNotCreateWikiError
     end
+
+    repository.after_create
+
+    wiki
   end
 
+  private
+
   def init_repo(path_with_namespace)
     gitlab_shell.add_repository(path_with_namespace)
   end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index e555e97689d3d1c04cffaee2490ea67af389ed5d..13154eb420583c53d956975e0580ddcfa4929767 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -3,6 +3,10 @@ require 'securerandom'
 class Repository
   class CommitError < StandardError; end
 
+  # Files to use as a project avatar in case no avatar was uploaded via the web
+  # UI.
+  AVATAR_FILES = %w{logo.png logo.jpg logo.gif}
+
   include Gitlab::ShellAdapter
 
   attr_accessor :path_with_namespace, :project
@@ -38,12 +42,15 @@ class Repository
   end
 
   def exists?
-    return false unless raw_repository
+    return @exists unless @exists.nil?
 
-    raw_repository.rugged
-    true
-  rescue Gitlab::Git::Repository::NoRepository
-    false
+    @exists = cache.fetch(:exists?) do
+      begin
+        raw_repository && raw_repository.rugged ? true : false
+      rescue Gitlab::Git::Repository::NoRepository
+        false
+      end
+    end
   end
 
   def empty?
@@ -223,12 +230,6 @@ class Repository
         send(key)
       end
     end
-
-    branches.each do |branch|
-      unless cache.exist?(:"diverging_commit_counts_#{branch.name}")
-        send(:diverging_commit_counts, branch)
-      end
-    end
   end
 
   def expire_tags_cache
@@ -241,12 +242,13 @@ class Repository
     @branches = nil
   end
 
-  def expire_cache(branch_name = nil)
+  def expire_cache(branch_name = nil, revision = nil)
     cache_keys.each do |key|
       cache.expire(key)
     end
 
     expire_branch_cache(branch_name)
+    expire_avatar_cache(branch_name, revision)
 
     # This ensures this particular cache is flushed after the first commit to a
     # new repository.
@@ -296,18 +298,6 @@ class Repository
     @tag_count = nil
   end
 
-  def rebuild_cache
-    cache_keys.each do |key|
-      cache.expire(key)
-      send(key)
-    end
-
-    branches.each do |branch|
-      cache.expire(:"diverging_commit_counts_#{branch.name}")
-      diverging_commit_counts(branch)
-    end
-  end
-
   def lookup_cache
     @lookup_cache ||= {}
   end
@@ -316,12 +306,40 @@ class Repository
     cache.expire(:branch_names)
   end
 
+  def expire_avatar_cache(branch_name = nil, revision = nil)
+    # Avatars are pulled from the default branch, thus if somebody pushes to a
+    # different branch there's no need to expire anything.
+    return if branch_name && branch_name != root_ref
+
+    # We don't want to flush the cache if the commit didn't actually make any
+    # changes to any of the possible avatar files.
+    if revision && commit = self.commit(revision)
+      return unless commit.diffs.
+        any? { |diff| AVATAR_FILES.include?(diff.new_path) }
+    end
+
+    cache.expire(:avatar)
+
+    @avatar = nil
+  end
+
+  def expire_exists_cache
+    cache.expire(:exists?)
+    @exists = nil
+  end
+
+  # Runs code after a repository has been created.
+  def after_create
+    expire_exists_cache
+  end
+
   # Runs code just before a repository is deleted.
   def before_delete
     expire_cache if exists?
 
     expire_root_ref_cache
     expire_emptiness_caches
+    expire_exists_cache
   end
 
   # Runs code just before the HEAD of a repository is changed.
@@ -347,11 +365,12 @@ class Repository
   # Runs code after a repository has been forked/imported.
   def after_import
     expire_emptiness_caches
+    expire_exists_cache
   end
 
   # Runs code after a new commit has been pushed.
-  def after_push_commit(branch_name)
-    expire_cache(branch_name)
+  def after_push_commit(branch_name, revision)
+    expire_cache(branch_name, revision)
   end
 
   # Runs code after a new branch has been created.
@@ -857,6 +876,14 @@ class Repository
     end
   end
 
+  def avatar
+    @avatar ||= cache.fetch(:avatar) do
+      AVATAR_FILES.find do |file|
+        blob_at_branch('master', file)
+      end
+    end
+  end
+
   private
 
   def cache
diff --git a/app/models/todo.rb b/app/models/todo.rb
index 5f91991f781811c79e8e85fbd40cca4582970f8e..d85f7bfdf57219688596245c6b4293f8ff5e18b9 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -5,14 +5,15 @@
 #  id          :integer          not null, primary key
 #  user_id     :integer          not null
 #  project_id  :integer          not null
-#  target_id   :integer          not null
+#  target_id   :integer
 #  target_type :string           not null
 #  author_id   :integer
-#  note_id     :integer
 #  action      :integer          not null
 #  state       :string           not null
 #  created_at  :datetime
 #  updated_at  :datetime
+#  note_id     :integer
+#  commit_id   :string
 #
 
 class Todo < ActiveRecord::Base
@@ -27,7 +28,9 @@ class Todo < ActiveRecord::Base
 
   delegate :name, :email, to: :author, prefix: true, allow_nil: true
 
-  validates :action, :project, :target, :user, presence: true
+  validates :action, :project, :target_type, :user, presence: true
+  validates :target_id, presence: true, unless: :for_commit?
+  validates :commit_id, presence: true, if: :for_commit?
 
   default_scope { reorder(id: :desc) }
 
@@ -36,7 +39,7 @@ class Todo < ActiveRecord::Base
 
   state_machine :state, initial: :pending do
     event :done do
-      transition [:pending, :done] => :done
+      transition [:pending] => :done
     end
 
     state :pending
@@ -50,4 +53,25 @@ class Todo < ActiveRecord::Base
       target.title
     end
   end
+
+  def for_commit?
+    target_type == "Commit"
+  end
+
+  # override to return commits, which are not active record
+  def target
+    if for_commit?
+      project.commit(commit_id) rescue nil
+    else
+      super
+    end
+  end
+
+  def target_reference
+    if for_commit?
+      target.short_id
+    else
+      target.to_reference
+    end
+  end
 end
diff --git a/app/models/user.rb b/app/models/user.rb
index c011af03591cdb9a70b46857651b32d86a172067..9c315cfe9662e1d11ee387418dca3e0e1f4b247f 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -435,7 +435,7 @@ class User < ActiveRecord::Base
     Group.where("namespaces.id IN (#{union.to_sql})")
   end
 
-  # Returns the groups a user is authorized to access.
+  # Returns projects user is authorized to access.
   def authorized_projects
     Project.where("projects.id IN (#{projects_union.to_sql})")
   end
diff --git a/app/services/base_service.rb b/app/services/base_service.rb
index 8563633816c192a73d86a47369cbe565470bfdee..0d55ba5a9816a944123058c034de268b9bd88f7b 100644
--- a/app/services/base_service.rb
+++ b/app/services/base_service.rb
@@ -43,12 +43,9 @@ class BaseService
   def deny_visibility_level(model, denied_visibility_level = nil)
     denied_visibility_level ||= model.visibility_level
 
-    level_name = Gitlab::VisibilityLevel.level_name(denied_visibility_level)
+    level_name = Gitlab::VisibilityLevel.level_name(denied_visibility_level).downcase
 
-    model.errors.add(
-      :visibility_level,
-      "#{level_name} visibility has been restricted by your GitLab administrator"
-    )
+    model.errors.add(:visibility_level, "#{level_name} has been restricted by your GitLab administrator")
   end
 
   private
diff --git a/app/services/ci/create_builds_service.rb b/app/services/ci/create_builds_service.rb
index 002f7ba12785aca3a496af1a3c9e8a4f90455526..2cd51a7610f6310e5c0c098244e74e2ddba27e0a 100644
--- a/app/services/ci/create_builds_service.rb
+++ b/app/services/ci/create_builds_service.rb
@@ -1,7 +1,7 @@
 module Ci
   class CreateBuildsService
     def execute(commit, stage, ref, tag, user, trigger_request, status)
-      builds_attrs = commit.config_processor.builds_for_stage_and_ref(stage, ref, tag)
+      builds_attrs = commit.config_processor.builds_for_stage_and_ref(stage, ref, tag, trigger_request)
 
       # check when to create next build
       builds_attrs = builds_attrs.select do |build_attrs|
diff --git a/app/services/commits/revert_service.rb b/app/services/commits/revert_service.rb
index 9cb918d7a2e9d95b37736605384df97ab4ebe538..a3c950ede1f51b33d60a417633da867646e0b138 100644
--- a/app/services/commits/revert_service.rb
+++ b/app/services/commits/revert_service.rb
@@ -9,7 +9,8 @@ module Commits
       @commit = params[:commit]
       @create_merge_request = params[:create_merge_request].present?
 
-      validate and commit
+      check_push_permissions unless @create_merge_request
+      commit
     rescue Repository::CommitError, Gitlab::Git::Repository::InvalidBlobName, GitHooksService::PreReceiveError,
            ValidationError, ReversionError => ex
       error(ex.message)
@@ -45,11 +46,11 @@ module Commits
       end
     end
 
-    def validate
+    def check_push_permissions
       allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(@target_branch)
 
       unless allowed
-        raise_error('You are not allowed to push into this branch')
+        raise ValidationError.new('You are not allowed to push into this branch')
       end
 
       true
diff --git a/app/services/create_snippet_service.rb b/app/services/create_snippet_service.rb
index 101a3df5eee44f8cdaa6151f88f16bb28582d2b5..9884cb9666168224caedba233c798f8047dd0225 100644
--- a/app/services/create_snippet_service.rb
+++ b/app/services/create_snippet_service.rb
@@ -6,8 +6,7 @@ class CreateSnippetService < BaseService
       snippet = project.snippets.build(params)
     end
 
-    unless Gitlab::VisibilityLevel.allowed_for?(current_user,
-                                                params[:visibility_level])
+    unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level])
       deny_visibility_level(snippet)
       return snippet
     end
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index d840ab5e340de453b8affb58c1c0401a3e98035d..c007d648dd692543f1e4c4b36a5f4743af0af74e 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -17,7 +17,7 @@ class GitPushService < BaseService
   #  6. Checks if the project's main language has changed
   #
   def execute
-    @project.repository.after_push_commit(branch_name)
+    @project.repository.after_push_commit(branch_name, params[:newrev])
 
     if push_remove_branch?
       @project.repository.after_remove_branch
@@ -120,7 +120,7 @@ class GitPushService < BaseService
         closed_issues = commit.closes_issues(current_user)
         closed_issues.each do |issue|
           if can?(current_user, :update_issue, issue)
-            Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit)
+            Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit: commit)
           end
         end
       end
diff --git a/app/services/groups/base_service.rb b/app/services/groups/base_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a8fa098246a52ce058d2ec581826c71ec51b7a96
--- /dev/null
+++ b/app/services/groups/base_service.rb
@@ -0,0 +1,9 @@
+module Groups
+  class BaseService < ::BaseService
+    attr_accessor :group, :current_user, :params
+
+    def initialize(group, user, params = {})
+      @group, @current_user, @params = group, user, params.dup
+    end
+  end
+end
diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2bccd584dde1e4c5eb023ba0d5248bc547db862d
--- /dev/null
+++ b/app/services/groups/create_service.rb
@@ -0,0 +1,21 @@
+module Groups
+  class CreateService < Groups::BaseService
+    def initialize(user, params = {})
+      @current_user, @params = user, params.dup
+    end
+
+    def execute
+      @group = Group.new(params)
+
+      unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level])
+        deny_visibility_level(@group)
+        return @group
+      end
+
+      @group.name ||= @group.path.dup
+      @group.save
+      @group.add_owner(current_user)
+      @group
+    end
+  end
+end
diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..99ad12b1003c3a068099055666d16e63110e9046
--- /dev/null
+++ b/app/services/groups/update_service.rb
@@ -0,0 +1,20 @@
+module Groups
+  class UpdateService < Groups::BaseService
+    def execute
+      # check that user is allowed to set specified visibility_level
+      new_visibility = params[:visibility_level]
+      if new_visibility && new_visibility.to_i != group.visibility_level
+        unless can?(current_user, :change_visibility_level, group) &&
+          Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
+
+          deny_visibility_level(group, new_visibility)
+          return group
+        end
+      end
+
+      group.assign_attributes(params)
+
+      group.save
+    end
+  end
+end
diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb
index 78254b49af37f901b397426826bccd3b58f5f6af..859c934ea3bf0493195fb6d106bb9c858b4baf13 100644
--- a/app/services/issues/close_service.rb
+++ b/app/services/issues/close_service.rb
@@ -1,6 +1,6 @@
 module Issues
   class CloseService < Issues::BaseService
-    def execute(issue, commit = nil)
+    def execute(issue, commit: nil, notifications: true, system_note: true)
       if project.jira_tracker? && project.jira_service.active
         project.jira_service.execute(commit, issue)
         todo_service.close_issue(issue, current_user)
@@ -9,8 +9,8 @@ module Issues
 
       if project.default_issues_tracker? && issue.close
         event_service.close_issue(issue, current_user)
-        create_note(issue, commit)
-        notification_service.close_issue(issue, current_user)
+        create_note(issue, commit) if system_note
+        notification_service.close_issue(issue, current_user) if notifications
         todo_service.close_issue(issue, current_user)
         execute_hooks(issue, 'close')
       end
diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb
index 10787e8873cedbe6a2a27493dfa625c2ebdfef61..e63e1af876640b87b95687a43a65baa0b980c4da 100644
--- a/app/services/issues/create_service.rb
+++ b/app/services/issues/create_service.rb
@@ -4,7 +4,7 @@ module Issues
       filter_params
       label_params = params[:label_ids]
       issue = project.issues.new(params.except(:label_ids))
-      issue.author = current_user
+      issue.author = params[:author] || current_user
 
       if issue.save
         issue.update_attributes(label_ids: label_params)
diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3cfbafe1576d1e375a78c0950314efb7726e81c5
--- /dev/null
+++ b/app/services/issues/move_service.rb
@@ -0,0 +1,94 @@
+module Issues
+  class MoveService < Issues::BaseService
+    class MoveError < StandardError; end
+
+    def execute(issue, new_project)
+      @old_issue = issue
+      @old_project = @project
+      @new_project = new_project
+
+      unless issue.can_move?(current_user, new_project)
+        raise MoveError, 'Cannot move issue due to insufficient permissions!'
+      end
+
+      if @project == new_project
+        raise MoveError, 'Cannot move issue to project it originates from!'
+      end
+
+      # Using transaction because of a high resources footprint
+      # on rewriting notes (unfolding references)
+      #
+      ActiveRecord::Base.transaction do
+        # New issue tasks
+        #
+        @new_issue = create_new_issue
+
+        rewrite_notes
+        add_note_moved_from
+
+        # Old issue tasks
+        #
+        add_note_moved_to
+        close_issue
+        mark_as_moved
+      end
+
+      notify_participants
+
+      @new_issue
+    end
+
+    private
+
+    def create_new_issue
+      new_params = { id: nil, iid: nil, label_ids: [], milestone: nil,
+                     project: @new_project, author: @old_issue.author,
+                     description: unfold_references(@old_issue.description) }
+
+      new_params = @old_issue.serializable_hash.merge(new_params)
+      CreateService.new(@new_project, @current_user, new_params).execute
+    end
+
+    def rewrite_notes
+      @old_issue.notes.find_each do |note|
+        new_note = note.dup
+        new_params = { project: @new_project, noteable: @new_issue,
+                       note: unfold_references(new_note.note),
+                       created_at: note.created_at }
+
+        new_note.update(new_params)
+      end
+    end
+
+    def close_issue
+      close_service = CloseService.new(@old_project, @current_user)
+      close_service.execute(@old_issue, notifications: false, system_note: false)
+    end
+
+    def add_note_moved_from
+      SystemNoteService.noteable_moved(@new_issue, @new_project,
+                                       @old_issue, @current_user,
+                                       direction: :from)
+    end
+
+    def add_note_moved_to
+      SystemNoteService.noteable_moved(@old_issue, @old_project,
+                                       @new_issue, @current_user,
+                                       direction: :to)
+    end
+
+    def unfold_references(content)
+      rewriter = Gitlab::Gfm::ReferenceRewriter.new(content, @old_project,
+                                                    @current_user)
+      rewriter.rewrite(@new_project)
+    end
+
+    def notify_participants
+      notification_service.issue_moved(@old_issue, @new_issue, @current_user)
+    end
+
+    def mark_as_moved
+      @old_issue.update(moved_to: @new_issue)
+    end
+  end
+end
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index 7b306a8a5319d3a347a0f19e550ca0962b41fd7d..ac5b58db8623755e252981c02b9e5bb5d6b43bae 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -5,6 +5,19 @@ module MergeRequests
       SystemNoteService.change_status(merge_request, merge_request.target_project, current_user, merge_request.state, nil)
     end
 
+    def create_title_change_note(issuable, old_title)
+      removed_wip = old_title =~ MergeRequest::WIP_REGEX && !issuable.work_in_progress?
+      added_wip = old_title !~ MergeRequest::WIP_REGEX && issuable.work_in_progress?
+
+      if removed_wip
+        SystemNoteService.remove_merge_request_wip(issuable, issuable.project, current_user)
+      elsif added_wip
+        SystemNoteService.add_merge_request_wip(issuable, issuable.project, current_user)
+      else
+        super
+      end
+    end
+
     def hook_data(merge_request, action)
       hook_data = merge_request.to_hook_data(current_user)
       merge_request_url = Gitlab::UrlBuilder.new(:merge_request).build(merge_request.id)
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index fa34753c4fd2d3e4f5b4c2fe0a98d30438a1df9f..6e9152e444ec6eb1eeed939ed678ef8534cedbce 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -51,7 +51,7 @@ module MergeRequests
       # be interpreted as the use wants to close that issue on this project
       # Pattern example: 112-fix-mep-mep
       # Will lead to appending `Closes #112` to the description
-      if match = merge_request.source_branch.match(/\A(\d+)-/)
+      if match = merge_request.source_branch.match(/-(\d+)\z/)
         iid = match[1]
         closes_issue = "Closes ##{iid}"
 
diff --git a/app/services/merge_requests/post_merge_service.rb b/app/services/merge_requests/post_merge_service.rb
index ebb67c7db65497f22a5b687abd1ba3c04f2c820d..064910f81f71bc486c024f3d76b13675d2973250 100644
--- a/app/services/merge_requests/post_merge_service.rb
+++ b/app/services/merge_requests/post_merge_service.rb
@@ -22,7 +22,7 @@ module MergeRequests
       closed_issues = merge_request.closes_issues(current_user)
       closed_issues.each do |issue|
         if can?(current_user, :update_issue, issue)
-          Issues::CloseService.new(project, current_user, {}).execute(issue, merge_request)
+          Issues::CloseService.new(project, current_user, {}).execute(issue, commit: merge_request)
         end
       end
     end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 19a6779dea98a6893058e233c91ecf58df93dbcc..3bdf00a8291cf56a3c633304a8c47d7d65b41a3f 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -236,6 +236,16 @@ class NotificationService
     end
   end
 
+  def issue_moved(issue, new_issue, current_user)
+    recipients = build_recipients(issue, issue.project, current_user)
+
+    recipients.map do |recipient|
+      email = mailer.issue_moved_email(recipient, issue, new_issue, current_user)
+      email.deliver_later
+      email
+    end
+  end
+
   protected
 
   # Get project users with WATCH notification level
diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb
index 7408e09ed1e9df60df177b33cade2521db187cda..ba50305dbd5fca2831c1534cd4a8a0eec4f94a57 100644
--- a/app/services/projects/autocomplete_service.rb
+++ b/app/services/projects/autocomplete_service.rb
@@ -1,11 +1,7 @@
 module Projects
   class AutocompleteService < BaseService
-    def initialize(project)
-      @project = project
-    end
-
     def issues
-      @project.issues.opened.select([:iid, :title])
+      @project.issues.visible_to_user(current_user).opened.select([:iid, :title])
     end
 
     def merge_requests
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index a6820183bee9ed83a3d94a3b9cb20933f9a00ec8..501e58c1407255087976bb335d676ef8af737fef 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -9,10 +9,8 @@ module Projects
 
       @project = Project.new(params)
 
-      # Make sure that the user is allowed to use the specified visibility
-      # level
-      unless Gitlab::VisibilityLevel.allowed_for?(current_user,
-                                                  params[:visibility_level])
+      # Make sure that the user is allowed to use the specified visibility level
+      unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level])
         deny_visibility_level(@project)
         return @project
       end
@@ -55,9 +53,7 @@ module Projects
         @project.save
 
         if @project.persisted? && !@project.import?
-          unless @project.create_repository
-            raise 'Failed to create repository'
-          end
+          raise 'Failed to create repository' unless @project.create_repository
         end
       end
 
diff --git a/app/services/projects/housekeeping_service.rb b/app/services/projects/housekeeping_service.rb
index bccd67d3dbf49df02c1cf15ca1ef104cd7eda8e1..a0973c5d2603ef1af0f25df03d6a7c2c803fb037 100644
--- a/app/services/projects/housekeeping_service.rb
+++ b/app/services/projects/housekeeping_service.rb
@@ -24,7 +24,7 @@ module Projects
     def execute
       raise LeaseTaken if !try_obtain_lease
 
-      GitlabShellWorker.perform_async(:gc, @project.path_with_namespace)
+      GitlabShellOneShotWorker.perform_async(:gc, @project.path_with_namespace)
     ensure
       @project.update_column(:pushes_since_gc, 0)
     end
diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb
index 895e089bea3c74f47cb41dd811efe5730cc09b5b..941df08995c9b5e4fe9bcd81c00f1374d6fa80a6 100644
--- a/app/services/projects/update_service.rb
+++ b/app/services/projects/update_service.rb
@@ -3,16 +3,13 @@ module Projects
     def execute
       # check that user is allowed to set specified visibility_level
       new_visibility = params[:visibility_level]
-      if new_visibility
-        if new_visibility.to_i != project.visibility_level
-          unless can?(current_user, :change_visibility_level, project) &&
-            Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
-            deny_visibility_level(project, new_visibility)
-            return project
-          end
+      if new_visibility && new_visibility.to_i != project.visibility_level
+        unless can?(current_user, :change_visibility_level, project) &&
+          Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
+          
+          deny_visibility_level(project, new_visibility)
+          return project
         end
-
-        return false unless visibility_level_allowed?(new_visibility)
       end
 
       new_branch = params[:default_branch]
@@ -27,19 +24,5 @@ module Projects
         end
       end
     end
-
-    private
-
-    def visibility_level_allowed?(level)
-      return true if project.visibility_level_allowed?(level)
-
-      level_name = Gitlab::VisibilityLevel.level_name(level)
-      project.errors.add(
-        :visibility_level,
-        "#{level_name} could not be set as visibility level of this project - parent project settings are more restrictive"
-      )
-
-      false
-    end
   end
 end
diff --git a/app/services/search/global_service.rb b/app/services/search/global_service.rb
index e1e94c5cc38dde3a444ab45f31a36079a2c984fd..aa9837038a6b4a9ba3b5732bec7d7eeba2a975bb 100644
--- a/app/services/search/global_service.rb
+++ b/app/services/search/global_service.rb
@@ -11,7 +11,7 @@ module Search
       projects = ProjectsFinder.new.execute(current_user)
       projects = projects.in_namespace(group.id) if group
 
-      Gitlab::SearchResults.new(projects, params[:search])
+      Gitlab::SearchResults.new(current_user, projects, params[:search])
     end
   end
 end
diff --git a/app/services/search/project_service.rb b/app/services/search/project_service.rb
index c08881dce4b8b2b398a9ebfb766f78c7d52b0958..4b500914cfbb4c7f006247c2a36826a28398c3a3 100644
--- a/app/services/search/project_service.rb
+++ b/app/services/search/project_service.rb
@@ -7,7 +7,8 @@ module Search
     end
 
     def execute
-      Gitlab::ProjectSearchResults.new(project,
+      Gitlab::ProjectSearchResults.new(current_user,
+                                       project,
                                        params[:search],
                                        params[:repository_ref])
     end
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index f09b77c4a571611bf7200eed25920e3a96bda210..e022a046c485013ac2da0270c9d5752e102315db 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -144,6 +144,18 @@ class SystemNoteService
     create_note(noteable: noteable, project: project, author: author, note: body)
   end
 
+  def self.remove_merge_request_wip(noteable, project, author)
+    body = 'Unmarked this merge request as a Work In Progress'
+
+    create_note(noteable: noteable, project: project, author: author, note: body)
+  end
+
+  def self.add_merge_request_wip(noteable, project, author)
+    body = 'Marked this merge request as a **Work In Progress**'
+
+    create_note(noteable: noteable, project: project, author: author, note: body)
+  end
+
   # Called when the title of a Noteable is changed
   #
   # noteable  - Noteable object that responds to `title`
@@ -210,7 +222,7 @@ class SystemNoteService
   # Called when a branch is created from the 'new branch' button on a issue
   # Example note text:
   #
-  #   "Started branch `201-issue-branch-button`"
+  #   "Started branch `issue-branch-button-201`"
   def self.new_issue_branch(issue, project, author, branch)
     h = Gitlab::Application.routes.url_helpers
     link = h.namespace_project_compare_url(project.namespace, project, from: project.default_branch, to: branch)
@@ -399,4 +411,26 @@ class SystemNoteService
     body = "Marked the task **#{new_task.source}** as #{status_label}"
     create_note(noteable: noteable, project: project, author: author, note: body)
   end
+
+  # Called when noteable has been moved to another project
+  #
+  # direction    - symbol, :to or :from
+  # noteable     - Noteable object
+  # noteable_ref - Referenced noteable
+  # author       - User performing the move
+  #
+  # Example Note text:
+  #
+  #   "Moved to some_namespace/project_new#11"
+  #
+  # Returns the created Note object
+  def self.noteable_moved(noteable, project, noteable_ref, author, direction:)
+    unless [:to, :from].include?(direction)
+      raise ArgumentError, "Invalid direction `#{direction}`"
+    end
+
+    cross_reference = noteable_ref.to_reference(project)
+    body = "Moved #{direction} #{cross_reference}"
+    create_note(noteable: noteable, project: project, author: author, note: body)
+  end
 end
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index 4392e2d17fe0e1aef464e40cf8bf407bcf2fbd22..f2662922e90549beca1046c7b4054dff480b1e3f 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -103,24 +103,16 @@ class TodoService
   #  * mark all pending todos related to the target for the current user as done
   #
   def mark_pending_todos_as_done(target, user)
-    pending_todos(user, target.project, target).update_all(state: :done)
+    attributes = attributes_for_target(target)
+    pending_todos(user, attributes).update_all(state: :done)
   end
 
   private
 
-  def create_todos(project, target, author, users, action, note = nil)
+  def create_todos(users, attributes)
     Array(users).each do |user|
-      next if pending_todos(user, project, target).exists?
-
-      Todo.create(
-        project: project,
-        user_id: user.id,
-        author_id: author.id,
-        target_id: target.id,
-        target_type: target.class.name,
-        action: action,
-        note: note
-      )
+      next if pending_todos(user, attributes).exists?
+      Todo.create(attributes.merge(user_id: user.id))
     end
   end
 
@@ -130,8 +122,8 @@ class TodoService
   end
 
   def handle_note(note, author)
-    # Skip system notes, notes on commit, and notes on project snippet
-    return if note.system? || ['Commit', 'Snippet'].include?(note.noteable_type)
+    # Skip system notes, and notes on project snippet
+    return if note.system? || note.for_project_snippet?
 
     project = note.project
     target  = note.noteable
@@ -142,13 +134,39 @@ class TodoService
 
   def create_assignment_todo(issuable, author)
     if issuable.assignee && issuable.assignee != author
-      create_todos(issuable.project, issuable, author, issuable.assignee, Todo::ASSIGNED)
+      attributes = attributes_for_todo(issuable.project, issuable, author, Todo::ASSIGNED)
+      create_todos(issuable.assignee, attributes)
     end
   end
 
-  def create_mention_todos(project, issuable, author, note = nil)
-    mentioned_users = filter_mentioned_users(project, note || issuable, author)
-    create_todos(project, issuable, author, mentioned_users, Todo::MENTIONED, note)
+  def create_mention_todos(project, target, author, note = nil)
+    mentioned_users = filter_mentioned_users(project, note || target, author)
+    attributes = attributes_for_todo(project, target, author, Todo::MENTIONED, note)
+    create_todos(mentioned_users, attributes)
+  end
+
+  def attributes_for_target(target)
+    attributes = {
+      project_id: target.project.id,
+      target_id: target.id,
+      target_type: target.class.name,
+      commit_id: nil
+    }
+
+    if target.is_a?(Commit)
+      attributes.merge!(target_id: nil, commit_id: target.id)
+    end
+
+    attributes
+  end
+
+  def attributes_for_todo(project, target, author, action, note = nil)
+    attributes_for_target(target).merge!(
+      project_id: project.id,
+      author_id: author.id,
+      action: action,
+      note: note
+    )
   end
 
   def filter_mentioned_users(project, target, author)
@@ -160,11 +178,8 @@ class TodoService
     mentioned_users.uniq
   end
 
-  def pending_todos(user, project, target)
-    user.todos.pending.where(
-      project_id: project.id,
-      target_id: target.id,
-      target_type: target.class.name
-    )
+  def pending_todos(user, criteria = {})
+    valid_keys = [:project_id, :target_id, :target_type, :commit_id]
+    user.todos.pending.where(criteria.slice(*valid_keys))
   end
 end
diff --git a/app/services/update_snippet_service.rb b/app/services/update_snippet_service.rb
index e9328bb7323a3daafaf406d98c1be86334fa74b3..93af8f21972716f84eec49bb9d3e04c7d9840ad8 100644
--- a/app/services/update_snippet_service.rb
+++ b/app/services/update_snippet_service.rb
@@ -9,7 +9,6 @@ class UpdateSnippetService < BaseService
   def execute
     # check that user is allowed to set specified visibility_level
     new_visibility = params[:visibility_level]
-
     if new_visibility && new_visibility.to_i != snippet.visibility_level
       unless Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
         deny_visibility_level(snippet, new_visibility)
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index b30dfd109ea181c95d7c8269ee2a18694b487f6a..0350995d03d652f108c240ae83f03e6d58c5eb62 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -19,6 +19,10 @@
       = f.label :default_snippet_visibility, class: 'control-label col-sm-2'
       .col-sm-10
         = render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new)
+    .form-group.group-visibility-level-holder
+      = f.label :default_group_visibility, class: 'control-label col-sm-2'
+      .col-sm-10
+        = render('shared/visibility_radios', model_method: :default_group_visibility, form: f, selected_level: @application_setting.default_group_visibility, form_model: Group.new)
     .form-group
       = f.label :restricted_visibility_levels, class: 'control-label col-sm-2'
       .col-sm-10
diff --git a/app/views/admin/applications/_delete_form.html.haml b/app/views/admin/applications/_delete_form.html.haml
index 3147cbd659f20a3efec6beee52498e3cd8feb8f3..042971e1eed1a4f5bd7d01049cd9a38ea1cc5ecb 100644
--- a/app/views/admin/applications/_delete_form.html.haml
+++ b/app/views/admin/applications/_delete_form.html.haml
@@ -1,4 +1,4 @@
 - submit_btn_css ||= 'btn btn-link btn-remove btn-sm'
 = form_tag admin_application_path(application) do
   %input{:name => "_method", :type => "hidden", :value => "delete"}/
-  = submit_tag 'Destroy', onclick: "return confirm('Are you sure?')", class: submit_btn_css
\ No newline at end of file
+  = submit_tag 'Destroy', onclick: "return confirm('Are you sure?')", class: submit_btn_css
diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml
index 198026a1f7517673a5165a706dad84ae94f00c7c..7f2b1cd235d990ce976cbea59d6dbef2fe0932a3 100644
--- a/app/views/admin/groups/_form.html.haml
+++ b/app/views/admin/groups/_form.html.haml
@@ -10,6 +10,8 @@
     .col-sm-10
       = render 'shared/choose_group_avatar_button', f: f
 
+  = render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group
+
   - if @group.new_record?
     .form-group
       .col-sm-offset-2.col-sm-10
diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml
index 118d3cfea0742d0bd396c94d0e6b21b245d964f9..6bdc885a3126505fd26099c2850b0355341dba30 100644
--- a/app/views/admin/groups/index.html.haml
+++ b/app/views/admin/groups/index.html.haml
@@ -46,6 +46,9 @@
 
         %h4
           = link_to [:admin, group] do
+            %span{ class: visibility_level_color(group.visibility_level) }
+              = visibility_level_icon(group.visibility_level)
+
             %i.fa.fa-folder
             = group.name
 
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 264fa1bf0cd12013a1ebd72d3e1708d0494edd04..f309e80a39a7d8a2b42530f9b3ed242552169be9 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -27,6 +27,11 @@
           %strong
             = @group.description
 
+        %li
+          %span.light Visibility level:
+          %strong
+            = visibility_level_label(@group.visibility_level)
+
         %li
           %span.light Created on:
           %strong
diff --git a/app/views/admin/labels/_label.html.haml b/app/views/admin/labels/_label.html.haml
index 5736a301910596866e63ae72392222419cec28ab..f417b2e44a411087ede12b3cbec7fdd9ec1698e6 100644
--- a/app/views/admin/labels/_label.html.haml
+++ b/app/views/admin/labels/_label.html.haml
@@ -1,6 +1,6 @@
 %li{id: dom_id(label)}
   .label-row
-    = render_colored_label(label)
+    = render_colored_label(label, tooltip: false)
     = markdown(label.description, pipeline: :single_line)
     .pull-right
       = link_to 'Edit', edit_admin_label_path(label), class: 'btn btn-sm'
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index d734e60682a31fa1a1d88c559ce1bdd4d6dfa424..c638c32a654ca59db7ade68ee4171c8f7106715c 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -52,7 +52,7 @@
           %li
             %span.light fs:
             %strong
-              = @repository.path_to_repo
+              = @project.repository.path_to_repo
 
           %li
             %span.light Size
diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml
index 45cfe3da188f5eda4b055958dea63895182423bd..e3a4d64df01e0412138594a62c276dc48191d605 100644
--- a/app/views/dashboard/todos/_todo.html.haml
+++ b/app/views/dashboard/todos/_todo.html.haml
@@ -2,7 +2,7 @@
   .todo-item.todo-block
     = image_tag avatar_icon(todo.author_email, 40), class: 'avatar s40', alt:''
 
-    .todo-title
+    .todo-title.title
       %span.author-name
         - if todo.author
           = link_to_author(todo)
@@ -16,7 +16,9 @@
 
     - if todo.pending?
       .todo-actions.pull-right
-        = link_to 'Done', [:dashboard, todo], method: :delete, class: 'btn'
+        = link_to [:dashboard, todo], method: :delete, class: 'btn btn-loading done-todo' do
+          Done
+          = icon('spinner spin')
 
     .todo-body
       .todo-note
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index 946d7df393383c6e65c5af7c566d3fee951b4a57..f9ec3a89158e13a8ee3836db4dff2df5e7181ed7 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -3,13 +3,15 @@
 
 .top-area
   %ul.nav-links
-    %li{class: ('active' if params[:state].blank? || params[:state] == 'pending')}
+    - todo_pending_active = ('active' if params[:state].blank? || params[:state] == 'pending')
+    %li{class: "todos-pending #{todo_pending_active}"}
       = link_to todos_filter_path(state: 'pending') do
         %span
           To do
         %span{class: 'badge'}
           = todos_pending_count
-    %li{class: ('active' if params[:state] == 'done')}
+    - 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
@@ -18,7 +20,9 @@
 
   .nav-controls
     - if @todos.any?(&:pending?)
-      = link_to 'Mark all as done', destroy_all_dashboard_todos_path(todos_filter_params), class: 'btn', method: :delete
+      = link_to destroy_all_dashboard_todos_path(todos_filter_params), class: 'btn btn-loading js-todos-mark-all', method: :delete do
+        Mark all as done
+        = icon('spinner spin')
 
 .todos-filters
   .gray-content-block.second-block
@@ -42,12 +46,12 @@
 .prepend-top-default
   - if @todos.any?
     - @todos.group_by(&:project).each do |group|
-      .panel.panel-default.panel-small
+      .panel.panel-default.panel-small.js-todos-list
         - project = group[0]
         .panel-heading
           = link_to project.name_with_namespace, namespace_project_path(project.namespace, project)
 
-        %ul.well-list.todos-list
+        %ul.content-list.todos-list
           = render group[1]
     = paginate @todos, theme: "gitlab"
   - else
diff --git a/app/views/devise/sessions/_new_crowd.html.haml b/app/views/devise/sessions/_new_crowd.html.haml
index 4974bb7f7fbfe43794801c01013f2dda2f1ad2de..8e81671b7e789485fc43b642df2723ec9e47ec3f 100644
--- a/app/views/devise/sessions/_new_crowd.html.haml
+++ b/app/views/devise/sessions/_new_crowd.html.haml
@@ -6,4 +6,4 @@
       %label{for: "remember_me"}
         = check_box_tag :remember_me, '1', false, id: 'remember_me'
         %span Remember me
-  = button_tag "Sign in", class: "btn-save btn"
\ No newline at end of file
+  = button_tag "Sign in", class: "btn-save btn"
diff --git a/app/views/doorkeeper/applications/new.html.haml b/app/views/doorkeeper/applications/new.html.haml
index fd32a468b4563441b89935b6caeefd03018a246c..d3692d1f759f80a949620f211784a531a00041df 100644
--- a/app/views/doorkeeper/applications/new.html.haml
+++ b/app/views/doorkeeper/applications/new.html.haml
@@ -4,4 +4,4 @@
 
 %hr
 
-= render 'form', application: @application
\ No newline at end of file
+= render 'form', application: @application
diff --git a/app/views/doorkeeper/authorizations/error.html.haml b/app/views/doorkeeper/authorizations/error.html.haml
index 7561ec85ed9563c07b4f2115872892cea55c8c4c..a4c607cea60c50b92b0518f17a21c3df08a707c2 100644
--- a/app/views/doorkeeper/authorizations/error.html.haml
+++ b/app/views/doorkeeper/authorizations/error.html.haml
@@ -1,3 +1,3 @@
 %h3.page-title An error has occurred
 %main{:role => "main"}
-  %pre= @pre_auth.error_response.body[:error_description]
\ No newline at end of file
+  %pre= @pre_auth.error_response.body[:error_description]
diff --git a/app/views/doorkeeper/authorizations/show.html.haml b/app/views/doorkeeper/authorizations/show.html.haml
index 9a402007194b0a898a1088b547cb21f77248ac62..01f9e46f1428f4326c0c98df9880b234e4f8e435 100644
--- a/app/views/doorkeeper/authorizations/show.html.haml
+++ b/app/views/doorkeeper/authorizations/show.html.haml
@@ -1,3 +1,3 @@
 %h3.page-title Authorization code:
 %main{:role => "main"}
-  %code#authorization_code= params[:code]
\ No newline at end of file
+  %code#authorization_code= params[:code]
diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml
index 36fb2d5162955f96ff9cdbc18818400fb22c333a..2d9d9dd634243eb44d8878f0851c065fff0446aa 100644
--- a/app/views/events/_event.html.haml
+++ b/app/views/events/_event.html.haml
@@ -1,4 +1,4 @@
-- if event.proper?
+- if event.proper?(current_user)
   .event-item{class: "#{event.body? ? "event-block" : "event-inline" }"}
     .event-item-timestamp
       #{time_ago_with_tooltip(event.created_at)}
diff --git a/app/views/events/event/_common.html.haml b/app/views/events/event/_common.html.haml
index e9e16a7646f4abf54a28e37b954b661b182bc4d6..c994e3b997de6b85e747f6872837e2e05995bb1c 100644
--- a/app/views/events/event/_common.html.haml
+++ b/app/views/events/event/_common.html.haml
@@ -4,7 +4,7 @@
     = event_action_name(event)
 
   - if event.target
-    %strong= link_to event.target.to_reference, [event.project.namespace.becomes(Namespace), event.project, event.target]
+    %strong= link_to event.target.reference_link_text, [event.project.namespace.becomes(Namespace), event.project, event.target]
 
   = event_preposition(event)
 
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index 83936d39b16fd3cc11b24d621f189b5bce6f0757..ea5a0358392911d17750a6b19987c0e4222a4dfb 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -23,6 +23,8 @@
             %hr
             = link_to 'Remove avatar', group_avatar_path(@group.to_param), data: { confirm: "Group avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar"
 
+      = render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group
+
       .form-group
         %hr
         = f.label :share_with_group_lock, class: 'control-label' do
@@ -32,6 +34,7 @@
             = f.check_box :share_with_group_lock
             %span.descr Prevent sharing a project with another group within this group
 
+
       .form-actions
         = f.submit 'Save group', class: "btn btn-save"
 
diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml
index 4bc31cabea61def06d9e0e50420ba3dce7c23548..30ab8aeba13e9d1706bf189bdf98190ae0e7395d 100644
--- a/app/views/groups/new.html.haml
+++ b/app/views/groups/new.html.haml
@@ -17,6 +17,8 @@
     .col-sm-10
       = render 'shared/choose_group_avatar_button', f: f
 
+  = render 'shared/visibility_level', f: f, visibility_level: default_group_visibility, can_change_visibility_level: true, form_model: @group
+
   .form-group
     .col-sm-offset-2.col-sm-10
       = render 'shared/group_tips'
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 23a34ac36dd006b95de628cdc72ec4db2de1c0c1..820743dc8dd8d63e6584fca37b4ecc78d487f3cc 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -1,8 +1,5 @@
 - @no_container = true
 
-- unless can?(current_user, :read_group, @group)
-  - @disable_search_panel = true
-
 = content_for :meta_tags do
   - if current_user
     = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity")
@@ -18,7 +15,10 @@
     = link_to group_icon(@group), target: '_blank' do
       = image_tag group_icon(@group), class: "avatar group-avatar s90"
   .cover-title
-    = @group.name
+    %h1
+      = @group.name
+      %span.visibility-icon.has_tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) }
+        = visibility_level_icon(@group.visibility_level, fw: false)
 
   .cover-desc.username
     @#{@group.path}
@@ -27,34 +27,29 @@
     .cover-desc.description
       = markdown(@group.description, pipeline: :description)
 
-- if can?(current_user, :read_group, @group)
-  %div{ class: container_class }
-    .top-area
-      %ul.nav-links
-        %li.active
-          = link_to "#projects", 'data-toggle' => 'tab' do
-            All Projects
-        - if @shared_projects.present?
-          %li
-            = link_to "#shared", 'data-toggle' => 'tab' do
-              Shared Projects
-      .nav-controls
-        = form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
-          = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
-        = render 'shared/projects/dropdown'
-        - if can? current_user, :create_projects, @group
-          = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new pull-right' do
-            = icon('plus')
-            New Project
-
-    .tab-content
-      .tab-pane.active#projects
-        = render "projects", projects: @projects
-
+%div{ class: container_class }
+  .top-area
+    %ul.nav-links
+      %li.active
+        = link_to "#projects", 'data-toggle' => 'tab' do
+          All Projects
       - if @shared_projects.present?
-        .tab-pane#shared
-          = render "shared_projects", projects: @shared_projects
-
-- else
-  %p.nav-links.no-top
-    No projects to show
+        %li
+          = link_to "#shared", 'data-toggle' => 'tab' do
+            Shared Projects
+    .nav-controls
+      = form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
+        = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
+      = render 'shared/projects/dropdown'
+      - if can? current_user, :create_projects, @group
+        = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new pull-right' do
+          = icon('plus')
+          New Project
+
+  .tab-content
+    .tab-pane.active#projects
+      = render "projects", projects: @projects
+
+    - if @shared_projects.present?
+      .tab-pane#shared
+        = render "shared_projects", projects: @shared_projects
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 77d01a7736c0339469041a1c4b9c226fa6714ed5..bfa5937cf3f3205209cc1ad405d20583066c65c5 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -7,9 +7,8 @@
 
       .navbar-collapse.collapse
         %ul.nav.navbar-nav.pull-right
-          - unless @disable_search_panel
-            %li.hidden-sm.hidden-xs
-              = render 'layouts/search'
+          %li.hidden-sm.hidden-xs
+            = render 'layouts/search'
           %li.visible-sm.visible-xs
             = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
               = icon('search')
@@ -46,6 +45,8 @@
       %h1.title= title
 
 = render 'shared/outdated_browser'
+
 - if @project && !@project.empty_repo?
-  :javascript
-    var findFileURL = "#{namespace_project_find_file_path(@project.namespace, @project, @ref || @project.repository.root_ref)}";
+  - if ref = @ref || @project.repository.root_ref
+    :javascript
+      var findFileURL = "#{namespace_project_find_file_path(@project.namespace, @project, ref)}";
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index db0cf393922dd7677ef06ac4f6aa5546a47f2f12..4a0069f18f8c1c8e5a6014e727318d961372a1f4 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -1,7 +1,7 @@
 %ul.nav.nav-sidebar
   = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: 'home'}) do
     = link_to dashboard_projects_path, title: 'Projects' do
-      = icon('home fw')
+      = icon('bookmark fw')
       %span
         Projects
   = nav_link(controller: :todos) do
diff --git a/app/views/layouts/nav/_explore.html.haml b/app/views/layouts/nav/_explore.html.haml
index 48039ca2918ffaeb1715c7077fbb6f3fa949406a..f08c5edf99c75c0a4c54b6f0055172906108e019 100644
--- a/app/views/layouts/nav/_explore.html.haml
+++ b/app/views/layouts/nav/_explore.html.haml
@@ -1,7 +1,7 @@
 %ul.nav.nav-sidebar
   = nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do
     = link_to explore_root_path, title: 'Projects' do
-      = icon('home fw')
+      = icon('bookmark fw')
       %span
         Projects
   = nav_link(controller: :groups) do
diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml
index 59411ae1da10f5f9fbe5d899c594967cfc4a0d23..55940741dc07e4ca59c67bd384097e37dc0f50bb 100644
--- a/app/views/layouts/nav/_group.html.haml
+++ b/app/views/layouts/nav/_group.html.haml
@@ -12,40 +12,38 @@
       = icon('group fw')
       %span
         Group
-  - if can?(current_user, :read_group, @group)
-    = nav_link(path: 'groups#activity') do
-      = link_to activity_group_path(@group), title: 'Activity' do
-        = icon('dashboard fw')
-        %span
-          Activity
-    - if current_user
-      = nav_link(controller: [:group, :milestones]) do
-        = link_to group_milestones_path(@group), title: 'Milestones' do
-          = icon('clock-o fw')
-          %span
-            Milestones
-    = nav_link(path: 'groups#issues') do
-      = link_to issues_group_path(@group), title: 'Issues' do
-        = icon('exclamation-circle fw')
-        %span
-          Issues
-          - if current_user
-            %span.count= number_with_delimiter(Issue.opened.of_group(@group).count)
-    = nav_link(path: 'groups#merge_requests') do
-      = link_to merge_requests_group_path(@group), title: 'Merge Requests' do
-        = icon('tasks fw')
-        %span
-          Merge Requests
-          - if current_user
-            %span.count= number_with_delimiter(MergeRequest.opened.of_group(@group).count)
-    = nav_link(controller: [:group_members]) do
-      = link_to group_group_members_path(@group), title: 'Members' do
-        = icon('users fw')
+  = nav_link(path: 'groups#activity') do
+    = link_to activity_group_path(@group), title: 'Activity' do
+      = icon('dashboard fw')
+      %span
+        Activity
+  = nav_link(controller: [:group, :milestones]) do
+    = link_to group_milestones_path(@group), title: 'Milestones' do
+      = icon('clock-o fw')
+      %span
+        Milestones
+  = nav_link(path: 'groups#issues') do
+    = link_to issues_group_path(@group), title: 'Issues' do
+      = icon('exclamation-circle fw')
+      %span
+        Issues
+        - issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
+        %span.count= number_with_delimiter(issues.count)
+  = nav_link(path: 'groups#merge_requests') do
+    = link_to merge_requests_group_path(@group), title: 'Merge Requests' do
+      = icon('tasks fw')
+      %span
+        Merge Requests
+        - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute
+        %span.count= number_with_delimiter(merge_requests.count)
+  = nav_link(controller: [:group_members]) do
+    = link_to group_group_members_path(@group), title: 'Members' do
+      = icon('users fw')
+      %span
+        Members
+  - if can?(current_user, :admin_group, @group)
+    = nav_link(html_options: { class: "separate-item" }) do
+      = link_to edit_group_path(@group), title: 'Settings' do
+        = icon ('cogs fw')
         %span
-          Members
-    - if can?(current_user, :admin_group, @group)
-      = nav_link(html_options: { class: "separate-item" }) do
-        = link_to edit_group_path(@group), title: 'Settings' do
-          = icon ('cogs fw')
-          %span
-            Settings
+          Settings
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 0ae83ee01ebc6bd519a6333dbcc446ab6d1d40f0..86b46e8c75ec33cf5f4be6463a0c0546f4d98740 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -67,7 +67,7 @@
         %span
           Issues
           - if @project.default_issues_tracker?
-            %span.count.issue_counter= number_with_delimiter(@project.issues.opened.count)
+            %span.count.issue_counter= number_with_delimiter(@project.issues.visible_to_user(current_user).opened.count)
 
   - if project_nav_tab? :merge_requests
     = nav_link(controller: :merge_requests) do
diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml
index 37b4d562966bdc80f57cf65dea1dc5bb1cdfc320..2997f59d946b4d97a4c815606ef7eb63e9752206 100644
--- a/app/views/layouts/notify.html.haml
+++ b/app/views/layouts/notify.html.haml
@@ -1,33 +1,9 @@
 %html{lang: "en"}
   %head
     %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"}
-      %title
-        GitLab
-  :css
-    img {
-      max-width: 100%;
-      height: auto;
-    }
-    p.details {
-      font-style:italic;
-      color:#777
-    }
-    .footer p {
-      font-size:small;
-      color:#777
-    }
-    pre.commit-message {
-      white-space: pre-wrap;
-    }
-    .file-stats a {
-      text-decoration: none;
-    }
-    .file-stats .new-file {
-      color: #090;
-    }
-    .file-stats .deleted-file {
-      color: #B00;
-    }
+    %title
+      GitLab
+    = stylesheet_link_tag 'notify'
   %body
     %div.content
       = yield
diff --git a/app/views/notify/issue_moved_email.html.haml b/app/views/notify/issue_moved_email.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..40f7d61fe19f6b15ebc95f4dc275bfa839ddbf44
--- /dev/null
+++ b/app/views/notify/issue_moved_email.html.haml
@@ -0,0 +1,6 @@
+%p
+  Issue was moved to another project.
+%p
+  New issue:
+  = link_to namespace_project_issue_url(@new_project.namespace, @new_project, @new_issue) do
+    = @new_issue.title
diff --git a/app/views/notify/issue_moved_email.text.erb b/app/views/notify/issue_moved_email.text.erb
new file mode 100644
index 0000000000000000000000000000000000000000..b3bd43c2055baf45882f66cff6e4c86cd3256dca
--- /dev/null
+++ b/app/views/notify/issue_moved_email.text.erb
@@ -0,0 +1,4 @@
+Issue was moved to another project.
+
+New issue location:
+<%= namespace_project_issue_url(@new_project.namespace, @new_project, @new_issue) %>
diff --git a/app/views/profiles/keys/_key.html.haml b/app/views/profiles/keys/_key.html.haml
index 25e9e8ff008f4137713f0a628df7616503a379dd..4dbaa662b66e7541c8ef0f24d0ce37cc5c075abd 100644
--- a/app/views/profiles/keys/_key.html.haml
+++ b/app/views/profiles/keys/_key.html.haml
@@ -8,7 +8,7 @@
       = key.fingerprint
   .pull-right
     %span.key-created-at
-      created #{time_ago_with_tooltip(key.created_at)} ago
+      created #{time_ago_with_tooltip(key.created_at)}
     = link_to path_to_key(key, is_admin), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-transparent prepend-left-10" do
       %span.sr-only Remove
       = icon('trash')
diff --git a/app/views/projects/_builds_settings.html.haml b/app/views/projects/_builds_settings.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..95ab9ecf3e842a3cb9c418e57300eaaa083ab8cf
--- /dev/null
+++ b/app/views/projects/_builds_settings.html.haml
@@ -0,0 +1,60 @@
+%fieldset.builds-feature
+  %legend
+    Builds:
+  .form-group
+    .col-sm-offset-2.col-sm-10
+      %p Get recent application code using the following command:
+      .radio
+        = f.label :build_allow_git_fetch_false do
+          = f.radio_button :build_allow_git_fetch, 'false'
+          %strong git clone
+          %br
+          %span.descr Slower but makes sure you have a clean dir before every build
+      .radio
+        = f.label :build_allow_git_fetch_true do
+          = f.radio_button :build_allow_git_fetch, 'true'
+          %strong git fetch
+          %br
+          %span.descr Faster
+
+  .form-group
+    = f.label :build_timeout_in_minutes, 'Timeout', class: 'control-label'
+    .col-sm-10
+      = f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0'
+      %p.help-block per build in minutes
+  .form-group
+    = f.label :build_coverage_regex, "Test coverage parsing", class: 'control-label'
+    .col-sm-10
+      .input-group
+        %span.input-group-addon /
+        = f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered'
+        %span.input-group-addon /
+      %p.help-block
+        We will use this regular expression to find test coverage output in build trace.
+        Leave blank if you want to disable this feature
+      .bs-callout.bs-callout-info
+        %p Below are examples of regex for existing tools:
+        %ul
+          %li
+            Simplecov (Ruby) -
+            %code \(\d+.\d+\%\) covered
+          %li
+            pytest-cov (Python) -
+            %code \d+\%\s*$
+          %li
+            phpunit --coverage-text --colors=never (PHP) -
+            %code ^\s*Lines:\s*\d+.\d+\%
+
+  .form-group
+    .col-sm-offset-2.col-sm-10
+      .checkbox
+        = f.label :public_builds do
+          = f.check_box :public_builds
+          %strong Public builds
+        .help-block Allow everyone to access builds for Public and Internal projects
+
+  .form-group
+    = f.label :runners_token, "Runners token", class: 'control-label'
+    .col-sm-10
+      = f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89'
+      %p.help-block The secure token used to checkout project.
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index b45df44f270238ed01123d897548a3269fb9bf41..514cbfa339dccb16626eaa50ec71efcce02a669d 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -2,21 +2,21 @@
 .project-home-panel.cover-block.clearfix{:class => ("empty-project" if empty_repo)}
   .project-identicon-holder
     = project_icon(@project, alt: '', class: 'project-avatar avatar s90')
-  .project-home-desc
+  .cover-title.project-home-desc
     %h1
       = @project.name
-      %span.visibility-icon.has_tooltip{data: { container: 'body' },
-        title: "#{visibility_level_label(@project.visibility_level)} - #{project_visibility_level_description(@project.visibility_level)}"}
+      %span.visibility-icon.has_tooltip{data: { container: 'body' }, title: visibility_icon_description(@project)}
         = visibility_level_icon(@project.visibility_level, fw: false)
 
-    - if @project.description.present?
+  - if @project.description.present?
+    .cover-desc.project-home-desc
       = markdown(@project.description, pipeline: :description)
 
-    - if forked_from_project = @project.forked_from_project
-      %p
-        Forked from
-        = link_to project_path(forked_from_project) do
-          = forked_from_project.namespace.try(:name)
+  - if forked_from_project = @project.forked_from_project
+    .cover-desc
+      Forked from
+      = link_to project_path(forked_from_project) do
+        = forked_from_project.namespace.try(:name)
 
   .cover-controls
     - if current_user
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index 76a823d382873484d2aec0e4b0783e89d995e4ee..57e507e68c80e4c9543bcbc108045dc9801fc8b9 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -11,7 +11,7 @@
     - if branch.name == @repository.root_ref
       %span.label.label-primary default
     - elsif @repository.merged_to_root_ref? branch.name
-      %span.label.label-info.has_tooltip(title="Merged into #{@repository.root_ref}")
+      %span.label.label-info.has-tooltip(title="Merged into #{@repository.root_ref}")
         merged
 
     - if @project.protected_branch? branch.name
@@ -30,7 +30,7 @@
           Compare
 
       - if can_remove_branch?(@project, branch.name)
-        = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has_tooltip', title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do
+        = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has-tooltip', title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do
           = icon("trash-o")
 
     - if branch.name != @repository.root_ref
diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml
index 6a60cfeff76ffea106f4cda782b26a41cbff70a3..58f43ecb5d5f2341d08bdc45110e394b67438511 100644
--- a/app/views/projects/buttons/_download.html.haml
+++ b/app/views/projects/buttons/_download.html.haml
@@ -1,4 +1,4 @@
 - unless @project.empty_repo?
   - if can? current_user, :download_code, @project
-    = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn has_tooltip', data: {container: "body"}, rel: 'nofollow', title: "Download ZIP" do
+    = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn has-tooltip', data: {container: "body"}, rel: 'nofollow', title: "Download ZIP" do
       = icon('download')
diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml
index 133531887a202cfabb18762643723d48d437c55f..88cbb7c03c58e4db2ebac9476be9633d297676c1 100644
--- a/app/views/projects/buttons/_fork.html.haml
+++ b/app/views/projects/buttons/_fork.html.haml
@@ -1,7 +1,7 @@
 - unless @project.empty_repo?
   - if current_user && can?(current_user, :fork_project, @project)
     - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
-      = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has_tooltip' do
+      = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has-tooltip' do
         = icon('code-fork fw')
         Fork
       %div.count-with-arrow
@@ -9,7 +9,7 @@
         %span.count
           = @project.forks_count
     - else
-      = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has_tooltip' do
+      = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has-tooltip' do
         = icon('code-fork fw')
         Fork
       %div.count-with-arrow
diff --git a/app/views/projects/buttons/_notifications.html.haml b/app/views/projects/buttons/_notifications.html.haml
index 3e83ec3912f6728996d70611e8d0da2b6cdc8f02..a3786c35a1f93400ec9e8d9d81e2c5116004b116 100644
--- a/app/views/projects/buttons/_notifications.html.haml
+++ b/app/views/projects/buttons/_notifications.html.haml
@@ -14,7 +14,7 @@
           = notification_list_item(level, @membership)
 
 - when GroupMember
-  .btn.disabled.notifications-btn.has_tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."}
+  .btn.disabled.notifications-btn.has-tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."}
     = icon('bell')
     = notification_label(@membership)
     = icon('angle-down')
diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml
index 21ba426aaa1e779a139aa29bc25ecc32be41c49f..02dbb2985a4b664460cf8b49b5c973a61c46e0ea 100644
--- a/app/views/projects/buttons/_star.html.haml
+++ b/app/views/projects/buttons/_star.html.haml
@@ -1,5 +1,5 @@
 - if current_user
-  = link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star has_tooltip', method: :post, remote: true, title: "Star project" do
+  = link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star has-tooltip', method: :post, remote: true, title: "Star project" do
     - if current_user.starred?(@project)
       = icon('star fw')
       %span.starred Unstar
@@ -12,7 +12,7 @@
       = @project.star_count
 
 - else
-  = link_to new_user_session_path, class: 'btn has_tooltip star-btn', title: 'You must sign in to star a project' do
+  = link_to new_user_session_path, class: 'btn has-tooltip star-btn', title: 'You must sign in to star a project' do
     = icon('star fw')
     Star
   %div.count-with-arrow
diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml
index 4ab81f3635cc37d19003bd153e486de07bcc17a2..dd590a4b8ec41ebff64ed9e5679322cc16f3fc4a 100644
--- a/app/views/projects/compare/_form.html.haml
+++ b/app/views/projects/compare/_form.html.haml
@@ -1,7 +1,7 @@
 = form_tag namespace_project_compare_index_path(@project.namespace, @project), method: :post, class: 'form-inline js-requires-input' do
   .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'}
+      = link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has-tooltip', title: 'Switch base of comparison'}
     .form-group
       .input-group.inline-input-group
         %span.input-group-addon from
diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml
index 6086ad3661e511311b78d7da2257f06b80b7b0f0..2e1a37aa06d28698482ea489579fbe8348ceefe4 100644
--- a/app/views/projects/diffs/_diffs.html.haml
+++ b/app/views/projects/diffs/_diffs.html.haml
@@ -20,4 +20,4 @@
     - next unless blob
 
     = render 'projects/diffs/file', i: index, project: project,
-      diff_file: diff_file, diff_commit: diff_commit, blob: blob
+      diff_file: diff_file, diff_commit: diff_commit, blob: blob, diff_refs: diff_refs
diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml
index 3ac058a3bf8c1b9a2b9088946adb1b4e4f021818..698ed02ea0e13fdb0111786ecb1b88fd77bbc24c 100644
--- a/app/views/projects/diffs/_file.html.haml
+++ b/app/views/projects/diffs/_file.html.haml
@@ -28,7 +28,7 @@
 
       .file-actions.hidden-xs
         - if blob_text_viewable?(blob)
-          = link_to '#', class: 'js-toggle-diff-comments btn active has_tooltip', title: "Toggle comments for this file" do
+          = link_to '#', class: 'js-toggle-diff-comments btn active has-tooltip', title: "Toggle comments for this file" do
             = icon('comments')
           \
 
@@ -42,13 +42,17 @@
   .diff-content.diff-wrap-lines
     -# Skipp all non non-supported blobs
     - return unless blob.respond_to?('text?')
-    - if blob_text_viewable?(blob)
-      - 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
-    - elsif blob.image?
-      - old_file = project.repository.prev_blob_for_diff(diff_commit, diff_file)
-      = render "projects/diffs/image", diff_file: diff_file, old_file: old_file, file: blob, index: i
+    - if diff_file.too_large?
+      .nothing-here-block
+        This diff could not be displayed because it is too large.
     - else
-      .nothing-here-block No preview for this file type
+      - if blob_text_viewable?(blob)
+        - 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
+      - elsif blob.image?
+        - old_file = project.repository.prev_blob_for_diff(diff_commit, diff_file)
+        = render "projects/diffs/image", diff_file: diff_file, old_file: old_file, file: blob, index: i, diff_refs: diff_refs
+      - else
+        .nothing-here-block No preview for this file type
diff --git a/app/views/projects/diffs/_image.html.haml b/app/views/projects/diffs/_image.html.haml
index 752e92e2e6bd4c97aeaa40b67b61b70afe7dd677..8367112a9cb93746c0e0010c41295c9f20cac56c 100644
--- a/app/views/projects/diffs/_image.html.haml
+++ b/app/views/projects/diffs/_image.html.haml
@@ -1,6 +1,7 @@
 - diff = diff_file.diff
 - file_raw_path = namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.id, diff.new_path))
-- old_file_raw_path = namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.parent_id, diff.old_path))
+- old_commit_id = diff_refs.first.id
+- old_file_raw_path = namespace_project_raw_path(@project.namespace, @project, tree_join(old_commit_id, diff.old_path))
 - if diff.renamed_file || diff.new_file || diff.deleted_file
   .image
     %span.wrap
@@ -12,7 +13,7 @@
     %div.two-up.view
       %span.wrap
         .frame.deleted
-          %a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(@commit.parent_id, diff.old_path))}
+          %a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(old_commit_id, diff.old_path))}
             %img{src: old_file_raw_path}
         %p.image-info.hide
           %span.meta-filesize= "#{number_to_human_size old_file.size}"
diff --git a/app/views/projects/diffs/_line.html.haml b/app/views/projects/diffs/_line.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..9464c8dc9965b9285584dae3b915117237a0cc17
--- /dev/null
+++ b/app/views/projects/diffs/_line.html.haml
@@ -0,0 +1,26 @@
+- type = line.type
+%tr.line_holder{id: line_code, class: type}
+  - case type
+  - when 'match'
+    = render "projects/diffs/match_line", {line: line.text,
+      line_old: line.old_pos, line_new: line.new_pos, bottom: false, new_file: diff_file.new_file}
+  - when 'nonewline'
+    %td.old_line.diff-line-num
+    %td.new_line.diff-line-num
+    %td.line_content.match= line.text
+  - else
+    %td.old_line.diff-line-num{class: type}
+      - link_text = raw(type == "new" ? "&nbsp;" : line.old_pos)
+      - if defined?(plain) && plain
+        = link_text
+      - else
+        = link_to link_text, "##{line_code}", id: line_code
+      - if @comments_allowed && can?(current_user, :create_note, @project)
+        = link_to_new_diff_note(line_code)
+    %td.new_line.diff-line-num{class: type, data: {linenumber: line.new_pos}}
+      - link_text = raw(type == "old" ? "&nbsp;" : line.new_pos)
+      - if defined?(plain) && plain
+        = link_text
+      - else
+        = link_to link_text, "##{line_code}", id: line_code
+    %td.line_content{class: "noteable_line #{type} #{line_code}", data: { line_code: line_code }}= diff_line_content(line.text)
diff --git a/app/views/projects/diffs/_text_file.html.haml b/app/views/projects/diffs/_text_file.html.haml
index 9a8208202e44662ea36305bb8a5a806f6196b3f8..e7169d7b599f74e4f8e3c0bacb34b8c10477ce0b 100644
--- a/app/views/projects/diffs/_text_file.html.haml
+++ b/app/views/projects/diffs/_text_file.html.haml
@@ -8,26 +8,9 @@
   - last_line = 0
   - raw_diff_lines = diff_file.diff_lines.to_a
   - diff_file.highlighted_diff_lines.each_with_index do |line, index|
-    - type = line.type
-    - last_line = line.new_pos
     - line_code = generate_line_code(diff_file.file_path, line)
-    - line_old = line.old_pos
-    %tr.line_holder{ id: line_code, class: "#{type}" }
-      - if type == "match"
-        = render "projects/diffs/match_line", {line: line.text,
-          line_old: line_old, line_new: line.new_pos, bottom: false, new_file: diff_file.new_file}
-      - elsif type == 'nonewline'
-        %td.old_line.diff-line-num
-        %td.new_line.diff-line-num
-        %td.line_content.match= line.text
-      - else
-        %td.old_line.diff-line-num{class: type}
-          = link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
-          - if @comments_allowed && can?(current_user, :create_note, @project)
-            = link_to_new_diff_note(line_code)
-        %td.new_line.diff-line-num{class: type, data: {linenumber: line.new_pos}}
-          = link_to raw(type == "old" ? "&nbsp;" : line.new_pos), "##{line_code}", id: line_code
-        %td.line_content{class: "noteable_line #{type} #{line_code}", data: { line_code: line_code }}= diff_line_content(line.text)
+    - last_line = line.new_pos
+    = render "projects/diffs/line", {line: line, diff_file: diff_file, line_code: line_code}
 
     - if @reply_allowed
       - comments = @line_notes.select { |n| n.line_code == line_code && n.active? }.sort_by(&:created_at)
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index f2e56081afe949aba3a562d9e708cff6dab1d127..6d872cd0b218839818a6ff258d261b8d4cec86b9 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -84,6 +84,8 @@
                     %br
                     %span.descr Share code pastes with others out of git repository
 
+          = render 'builds_settings', f: f
+
           %fieldset.features
             %legend
               Project avatar:
@@ -110,69 +112,6 @@
                   %hr
                   = link_to 'Remove avatar', namespace_project_avatar_path(@project.namespace, @project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar"
 
-          %fieldset.features
-            %legend
-              Continuous Integration
-            .form-group
-              .col-sm-offset-2.col-sm-10
-                %p Get recent application code using the following command:
-                .radio
-                  = f.label :build_allow_git_fetch_false do
-                    = f.radio_button :build_allow_git_fetch, 'false'
-                    %strong git clone
-                    %br
-                    %span.descr Slower but makes sure you have a clean dir before every build
-                .radio
-                  = f.label :build_allow_git_fetch_true do
-                    = f.radio_button :build_allow_git_fetch, 'true'
-                    %strong git fetch
-                    %br
-                    %span.descr Faster
-
-            .form-group
-              = f.label :build_timeout_in_minutes, 'Timeout', class: 'control-label'
-              .col-sm-10
-                = f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0'
-                %p.help-block per build in minutes
-            .form-group
-              = f.label :build_coverage_regex, "Test coverage parsing", class: 'control-label'
-              .col-sm-10
-                .input-group
-                  %span.input-group-addon /
-                  = f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered'
-                  %span.input-group-addon /
-                %p.help-block
-                  We will use this regular expression to find test coverage output in build trace.
-                  Leave blank if you want to disable this feature
-                .bs-callout.bs-callout-info
-                  %p Below are examples of regex for existing tools:
-                  %ul
-                    %li
-                      Simplecov (Ruby) -
-                      %code \(\d+.\d+\%\) covered
-                    %li
-                      pytest-cov (Python) -
-                      %code \d+\%\s*$
-                    %li
-                      phpunit --coverage-text --colors=never (PHP) -
-                      %code ^\s*Lines:\s*\d+.\d+\%
-
-              .form-group
-                .col-sm-offset-2.col-sm-10
-                  .checkbox
-                    = f.label :public_builds do
-                      = f.check_box :public_builds
-                      %strong Public builds
-                    .help-block Allow everyone to access builds for Public and Internal projects
-
-          %fieldset.features
-            %legend
-              Advanced settings
-            .form-group
-              = f.label :runners_token, "CI token", class: 'control-label'
-              .col-sm-10
-                = f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89'
-                %p.help-block The secure token used to checkout project.
 
           .form-actions
             = f.submit 'Save changes', class: "btn btn-save"
diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml
index edabc2d3b44e9bed2d4f982b412ddbad54026ecd..73a7fc0e1ac66c5b92ff395dd5801d4676b3b70b 100644
--- a/app/views/projects/forks/new.html.haml
+++ b/app/views/projects/forks/new.html.haml
@@ -12,7 +12,7 @@
           .col-md-2.col-sm-3
             - if fork = namespace.find_fork_of(@project)
               .fork-thumbnail
-                = link_to project_path(fork), title: "Visit project fork", class: 'has_tooltip' do
+                = link_to project_path(fork), title: "Visit project fork", class: 'has-tooltip' do
                   = image_tag namespace_icon(namespace, 100)
                   .caption
                     %strong
@@ -22,7 +22,7 @@
 
             - else
               .fork-thumbnail
-                = link_to namespace_project_forks_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has_tooltip' do
+                = link_to namespace_project_forks_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has-tooltip' do
                   = image_tag namespace_icon(namespace, 100)
                   .caption
                     %strong
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index a44f34c2a6843fbd3886598060842d19074a40c6..4aa92d0b39e2470046354cc9ff8d860286806bf1 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -3,10 +3,11 @@
     .issue-check
       = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue"
 
-  .issue-title
+  .issue-title.title
     %span.issue-title-text
-      = link_to_gfm issue.title, issue_path(issue), class: "title"
-    %ul.controls.light
+      = confidential_icon(issue)
+      = link_to_gfm issue.title, issue_path(issue)
+    %ul.controls
       - if issue.closed?
         %li
           CLOSED
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 1e8308277cc1e6d6a973bf33b9a3d43b6570c05a..6fa059cbe68a179dbb0c886ac661354671ffcbf1 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -22,20 +22,20 @@
       = icon('angle-double-left')
 
     .issue-meta
+      = confidential_icon(@issue)
       %strong.identifier
         Issue ##{@issue.iid}
       %span.creator
-        by
+        opened
         .editor-details
           .editor-details
+            = time_ago_with_tooltip(@issue.created_at)
+            by
             %strong
               = link_to_member(@project, @issue.author, size: 24, mobile_classes: "hidden-xs")
-            %span.hidden-xs
-              = '@' + @issue.author.username
             %strong
               = link_to_member(@project, @issue.author, size: 24, mobile_classes: "hidden-sm hidden-md hidden-lg",
                 by_username: true, avatar: false)
-            = time_ago_with_tooltip(@issue.created_at)
 
     .pull-right.issue-btn-group
       - if can?(current_user, :create_issue, @project)
@@ -45,7 +45,6 @@
       - if can?(current_user, :update_issue, @issue)
         = link_to 'Reopen issue', issue_path(@issue, issue: {state_event: :reopen}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn btn-nr btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
         = link_to 'Close issue', issue_path(@issue, issue: {state_event: :close}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn btn-nr btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
-
         = link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'btn btn-nr btn-grouped issuable-edit' do
           = icon('pencil-square-o')
           Edit
@@ -63,10 +62,7 @@
                 = markdown(@issue.description, cache_key: [@issue, "description"])
             %textarea.hidden.js-task-list-field
               = @issue.description
-      - if @issue.updated_at != @issue.created_at
-        %small
-          Edited
-          = time_ago_with_tooltip(@issue.updated_at, placement: 'bottom', html_class: 'issue_edited_ago')
+      = edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue_edited_ago')
 
       .merge-requests
         = render 'merge_requests'
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 18cf3f14f0bf34e5c12b714561a049f1aa4f26d1..391193eed6c6c22abe2878386ee143878632326a 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -1,8 +1,8 @@
 %li{ class: mr_css_classes(merge_request) }
-  .merge-request-title
+  .merge-request-title.title
     %span.merge-request-title-text
-      = link_to_gfm merge_request.title, merge_request_path(merge_request), class: "title"
-    %ul.controls.light
+      = link_to_gfm merge_request.title, merge_request_path(merge_request)
+    %ul.controls
       - if merge_request.merged?
         %li
           MERGED
@@ -17,7 +17,7 @@
 
       - if merge_request.open? && merge_request.broken?
         %li
-          = link_to merge_request_path(merge_request), class: "has_tooltip", title: "Cannot be merged automatically", data: { container: 'body' } do
+          = link_to merge_request_path(merge_request), class: "has-tooltip", title: "Cannot be merged automatically", data: { container: 'body' } do
             = icon('exclamation-triangle')
 
       - if merge_request.assignee
diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml
index 602f787e6cfdb685c2bc3e49781b4a07d7a43027..a23bd8d18d0512f8969d40d6b822c02d52d2a1f9 100644
--- a/app/views/projects/merge_requests/show/_mr_box.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_box.html.haml
@@ -11,7 +11,4 @@
         %textarea.hidden.js-task-list-field
           = @merge_request.description
 
-  - if @merge_request.updated_at != @merge_request.created_at
-    %small
-      Edited
-      = time_ago_with_tooltip(@merge_request.updated_at, placement: 'bottom')
+  = edited_time_ago_with_tooltip(@merge_request, placement: 'bottom')
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 a75c0d96c5767fbcbb66a34be7657a5e11da7426..ab4b1f14be5ab6146e008fdf7d92ef3ed8b804f1 100644
--- a/app/views/projects/merge_requests/show/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_title.html.haml
@@ -8,25 +8,28 @@
     = icon('angle-double-left')
   .issue-meta
     %strong.identifier
-      Merge Request ##{@merge_request.iid}
+      %span.hidden-sm.hidden-md.hidden-lg
+        MR
+      %span.hidden-xs
+        Merge Request
+      !#{@merge_request.iid}
     %span.creator
-      by
+      opened
       .editor-details
+        = time_ago_with_tooltip(@merge_request.created_at)
+        by
         %strong
           = link_to_member(@project, @merge_request.author, size: 24, mobile_classes: "hidden-xs")
-        %span.hidden-xs
-          = '@' + @merge_request.author.username
         %strong
           = link_to_member(@project, @merge_request.author, size: 24, mobile_classes: "hidden-sm hidden-md hidden-lg",
             by_username: true, avatar: false)
-        = time_ago_with_tooltip(@merge_request.created_at)
 
   .issue-btn-group.pull-right
     - if can?(current_user, :update_merge_request, @merge_request)
       - if @merge_request.open?
         = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: 'btn btn-nr btn-grouped btn-close', title: 'Close merge request'
         = link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-nr btn-grouped issuable-edit', id: 'edit_merge_request' do
-          %i.fa.fa-pencil-square-o
+          = icon('pencil-square-o')
           Edit
       - if @merge_request.closed?
         = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: 'btn btn-nr btn-grouped btn-reopen reopen-mr-link', title: 'Reopen merge request'
diff --git a/app/views/projects/merge_requests/widget/open/_wip.html.haml b/app/views/projects/merge_requests/widget/open/_wip.html.haml
index 0cf16542cc1823cc7e09f6799bc5fce5e2fac926..c296422a9cf5c01b5efda51cc06df9eeac440a51 100644
--- a/app/views/projects/merge_requests/widget/open/_wip.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_wip.html.haml
@@ -1,5 +1,11 @@
 %h4
   This merge request is currently a Work In Progress
 
-%p
-  When this merge request is ready, remove the "WIP" prefix from the title to allow it to be merged.
+- if can?(current_user, :update_merge_request, @merge_request)
+  %p
+    When this merge request is ready,
+    = link_to remove_wip_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), method: :post do
+      remove the
+      %code WIP:
+      prefix from the title
+    to allow it to be merged.
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index b4597043a27af1ec6a0fc1cc541e056e4da12886..be63875ab34c0ab1f97104adb9fb6df13d8d478c 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -42,7 +42,7 @@
           = preserve do
             = markdown @milestone.description
 
-- if @milestone.complete? && @milestone.active?
+- if @milestone.complete?(current_user) && @milestone.active?
   .alert.alert-success.prepend-top-default
     %span All issues for this milestone are closed. You may close milestone now.
 
diff --git a/app/views/projects/notes/_edit_form.html.haml b/app/views/projects/notes/_edit_form.html.haml
index 13e624764d9b8b015dae67bf0e69e2e5e8dbec72..2999befffc642be5723a25c3c90e47c1f6211ec3 100644
--- a/app/views/projects/notes/_edit_form.html.haml
+++ b/app/views/projects/notes/_edit_form.html.haml
@@ -5,6 +5,6 @@
       = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field'
       = render 'projects/notes/hints'
 
-    .note-form-actions
+    .note-form-actions.clearfix
       = f.submit 'Save Comment', class: 'btn btn-nr btn-save btn-grouped js-comment-button'
       = link_to  'Cancel', '#', class: 'btn btn-nr btn-cancel note-edit-cancel'
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index 52972576aff1f25ecac81da658998e297fda1195..2cf32e6093dec09b3e7749a11041bca74fcf872f 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -27,20 +27,13 @@
         %span.note-last-update
           %a{name: dom_id(note), href: "##{dom_id(note)}", title: 'Link here'}
             = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note_created_ago')
-          - if note.updated_at != note.created_at
-            %span.note-updated-at
-              &middot;
-              = icon('edit', title: 'edited')
-              = time_ago_with_tooltip(note.updated_at, placement: 'bottom', html_class: 'note_edited_ago')
-              - if note.updated_by && note.updated_by != note.author
-                by #{link_to_member(note.project, note.updated_by, avatar: false, author_class: nil)}
-
       .note-body{class: note_editable?(note) ? 'js-task-list-container' : ''}
         .note-text
           = preserve do
             = markdown(note.note, pipeline: :note, cache_key: [note, "note"])
         - if note_editable?(note)
           = render 'projects/notes/edit_form', note: note
+      = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true)
 
       - if note.attachment.url
         .note-attachment
@@ -54,4 +47,3 @@
               = link_to delete_attachment_namespace_project_note_path(note.project.namespace, note.project, note),
                 title: 'Delete this attachment', method: :delete, remote: true, data: { confirm: 'Are you sure you want to remove the attachment?' }, class: 'danger js-note-attachment-delete' do
                 = icon('trash-o', class: 'cred')
-      .clear
diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml
index b9486a9b49265c92771b58c529b26fd9f51cb4de..24658319060b7c5f4fd4eb2bc786b3bb2e260eef 100644
--- a/app/views/projects/repositories/_download_archive.html.haml
+++ b/app/views/projects/repositories/_download_archive.html.haml
@@ -10,7 +10,7 @@
       %span.caret
       %span.sr-only
         Select Archive Format
-    %ul.col-xs-10.dropdown-menu{ role: 'menu' }
+    %ul.col-xs-10.dropdown-menu.dropdown-menu-align-right{ role: 'menu' }
       %li
         = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), rel: 'nofollow' do
           %i.fa.fa-download
diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml
index 399782273d308f233168814ae873b4c79096b4d0..dbc35c16febac6f9b7b942f560f1a0bbfa69fa7e 100644
--- a/app/views/projects/tags/_tag.html.haml
+++ b/app/views/projects/tags/_tag.html.haml
@@ -15,11 +15,11 @@
         = render 'projects/tags/download', ref: tag.name, project: @project
 
       - if can?(current_user, :push_code, @project)
-        = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn has_tooltip', title: "Edit release notes" do
+        = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn has-tooltip', title: "Edit release notes" do
           = icon("pencil")
 
       - if can?(current_user, :admin_project, @project)
-        = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has_tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do
+        = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do
           = icon("trash-o")
 
   - if commit
diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml
index 8c7f93f93b683b7c2ab588849ebc6acb1b00463a..1dc9b799a95db2f5a925ffb96a5fb17bd09af890 100644
--- a/app/views/projects/tags/show.html.haml
+++ b/app/views/projects/tags/show.html.haml
@@ -5,17 +5,17 @@
 .gray-content-block
   .pull-right
     - if can?(current_user, :push_code, @project)
-      = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn has_tooltip', title: 'Edit release notes' do
+      = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn has-tooltip', title: 'Edit release notes' do
         = icon("pencil")
-    = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has_tooltip', title: 'Browse files' do
+    = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has-tooltip', title: 'Browse files' do
       = icon('files-o')
-    = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has_tooltip', title: 'Browse commits' do
+    = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has-tooltip', title: 'Browse commits' do
       = icon('history')
     - if can? current_user, :download_code, @project
       = render 'projects/tags/download', ref: @tag.name, project: @project
     - if can?(current_user, :admin_project, @project)
       .pull-right
-        = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has_tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do
+        = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do
           %i.fa.fa-trash-o
   .title
     %span.item-title= @tag.name
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index 3eb626e6dcac84f3ef86c54125e85e01527e2e2a..ba69569b1e75d18f124d7f0b65341ca7d83604db 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -15,7 +15,7 @@
   - if current_user
     %li
       - if !on_top_of_branch?
-        %span.btn.btn-sm.add-to-tree.disabled.has_tooltip{title: "You can only add files when you are on a branch", data: { container: 'body' }}
+        %span.btn.btn-sm.add-to-tree.disabled.has-tooltip{title: "You can only add files when you are on a branch", data: { container: 'body' }}
           = icon('plus')
       - else
         %span.dropdown
diff --git a/app/views/search/results/_issue.html.haml b/app/views/search/results/_issue.html.haml
index 45d700781f3e3d14547268ef9444810112a8e32e..710f5613c817238893531ba540af9573f796150d 100644
--- a/app/views/search/results/_issue.html.haml
+++ b/app/views/search/results/_issue.html.haml
@@ -1,5 +1,6 @@
 .search-result-row
   %h4
+    = confidential_icon(issue)
     = link_to [issue.project.namespace.becomes(Namespace), issue.project, issue] do
       %span.term.str-truncated= issue.title
     .pull-right ##{issue.iid}
diff --git a/app/views/search/results/_milestone.html.haml b/app/views/search/results/_milestone.html.haml
index e0b18733d742e6319cb25d900eb0384cff15e1e5..b31595d8d1c5a4fe9dc4c2a9ce115ff559650287 100644
--- a/app/views/search/results/_milestone.html.haml
+++ b/app/views/search/results/_milestone.html.haml
@@ -6,4 +6,4 @@
   - if milestone.description.present?
     .description.term
       = preserve do
-        = search_md_sanitize(markdown(milestone.description))
\ No newline at end of file
+        = search_md_sanitize(markdown(milestone.description))
diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index faf7e49ed2902fb6c43a0f54cae83ea9fb558fe7..974751d99705d45e740466a622db1c0a2370ab89 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -8,11 +8,9 @@
       = icon('angle-down')
     %ul.dropdown-menu.dropdown-menu-right.clone-options-dropdown
       %li
-        %a#ssh-selector{href: @project.ssh_url_to_repo}
-          SSH
+        = ssh_clone_button(project)
       %li
-        %a#http-selector{href: @project.http_url_to_repo}
-          HTTPS
+        = http_clone_button(project)
 
   = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true
   .input-group-btn
diff --git a/app/views/shared/_group_tips.html.haml b/app/views/shared/_group_tips.html.haml
index e5cf783beb7ae21505440dfc6296c159bd382a74..46e4340511a5b18ef002f195660b6f97d330e731 100644
--- a/app/views/shared/_group_tips.html.haml
+++ b/app/views/shared/_group_tips.html.haml
@@ -1,6 +1,5 @@
 %ul
   %li A group is a collection of several projects
-  %li Groups are private by default
   %li Members of a group may only view projects they have permission to access
   %li Group project URLs are prefixed with the group namespace
   %li Existing projects may be moved into a group
diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml
index 8134b15d24534de35ec312ca44e8dbc5b0c233f7..4b47b0291be69a334ad44bdbd2f1a19d7f25415a 100644
--- a/app/views/shared/_label_row.html.haml
+++ b/app/views/shared/_label_row.html.haml
@@ -1,4 +1,4 @@
 %span.label-row
-  = link_to_label(label)
+  = link_to_label(label, tooltip: false)
   %span.prepend-left-10
     = markdown(label.description, pipeline: :single_line)
diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml
index fb9a8db08893299c53db296e76f9e586fea3a009..66b7ef996509a6ae4f570c8e67acf5500a811e9a 100644
--- a/app/views/shared/groups/_group.html.haml
+++ b/app/views/shared/groups/_group.html.haml
@@ -10,24 +10,28 @@
           %i.fa.fa-cogs
 
       = link_to leave_group_group_members_path(group), data: { confirm: leave_group_message(group.name) }, method: :delete, class: "btn-sm btn btn-grouped", title: 'Leave this group' do
-        %i.fa.fa-sign-out
+        = icon('sign-out')
 
   .stats
     %span
-      = icon('home')
+      = 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"
-  = link_to group, class: 'group-name title' do
-    = group.name
+  .title
+    = link_to group, class: 'group-name' do
+      = group.name
 
-  - if group_member
-    as
-    %span #{group_member.human_access}
+    - if group_member
+      as
+      %span #{group_member.human_access}
 
   - if group.description.present?
     .description
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index 3eb0db276b201a49376b2368db207345a6e4cd63..ac20f7d1f7e39d3d26a60cb830e2f7e8215da9fd 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -9,75 +9,20 @@
         .filter-item.inline
           - if params[:author_id]
             = hidden_field_tag(:author_id, params[:author_id])
-          = dropdown_tag("Author", options: { toggle_class: "js-user-search js-filter-submit js-author-search", title: "Filter by author", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-author",
-            placeholder: "Search authors", data: { any_user: "Any Author", first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author_id], field_name: "author_id" } })
+          = dropdown_tag(user_dropdown_label(params[:author_id], "Author"), options: { toggle_class: "js-user-search js-filter-submit js-author-search", title: "Filter by author", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-author",
+            placeholder: "Search authors", data: { any_user: "Any Author", first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author_id], field_name: "author_id", default_label: "Author" } })
 
         .filter-item.inline
           - if params[:assignee_id]
             = hidden_field_tag(:assignee_id, params[:assignee_id])
-          = dropdown_tag("Assignee", options: { toggle_class: "js-user-search js-filter-submit js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee",
-            placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (@project.id if @project), selected: params[:assignee_id], field_name: "assignee_id" } })
+          = dropdown_tag(user_dropdown_label(params[:assignee_id], "Assignee"), options: { toggle_class: "js-user-search js-filter-submit js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee",
+            placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (@project.id if @project), selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } })
 
         .filter-item.inline.milestone-filter
-          - if params[:milestone_title]
-            = hidden_field_tag(:milestone_title, params[:milestone_title])
-          = dropdown_tag("Milestone", options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit', filter: true, dropdown_class: "dropdown-menu-selectable",
-            placeholder: "Search milestones", footer_content: true, data: { show_no: true, show_any: true, field_name: "milestone_title", selected: params[:milestone_title], project_id: (@project.id if @project), milestones: (namespace_project_milestones_path(@project.namespace, @project, :js) if @project) } }) do
-            - if @project
-              %ul.dropdown-footer-list
-                - if can? current_user, :admin_milestone, @project
-                  %li
-                    = link_to new_namespace_project_milestone_path(@project.namespace, @project), title: "New Milestone" do
-                      Create new
-                %li
-                  = link_to namespace_project_milestones_path(@project.namespace, @project) do
-                    - if can? current_user, :admin_milestone, @project
-                      Manage milestones
-                    - else
-                      View milestones
+          = render "shared/issuable/milestone_dropdown"
 
         .filter-item.inline.labels-filter
-          - if params[:label_name]
-            = hidden_field_tag(:label_name, params[:label_name])
-          .dropdown
-            %button.dropdown-menu-toggle.js-label-select.js-filter-submit{type: "button", data: {toggle: "dropdown", field_name: "label_name", show_no: "true", show_any: "true", selected: params[:label_name], project_id: (@project.id if @project), labels: (namespace_project_labels_path(@project.namespace, @project, :js) if @project)}}
-              %span.dropdown-toggle-text
-                Label
-              = icon('chevron-down')
-            .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
-              .dropdown-page-one
-                = dropdown_title("Filter by label")
-                = dropdown_filter("Search labels")
-                = dropdown_content
-                - if @project
-                  = dropdown_footer do
-                    %ul.dropdown-footer-list
-                      - if can? current_user, :admin_label, @project
-                        %li
-                          %a.dropdown-toggle-page{href: "#"}
-                            Create new
-                      %li
-                        = link_to namespace_project_labels_path(@project.namespace, @project) do
-                          - if can? current_user, :admin_label, @project
-                            Manage labels
-                          - else
-                            View labels
-              - if can? current_user, :admin_label, @project
-                .dropdown-page-two
-                  = dropdown_title("Create new label", back: true)
-                  = dropdown_content do
-                    %input#new_label_color{type: "hidden"}
-                    %input#new_label_name.dropdown-input-field{type: "text", placeholder: "Name new label"}
-                    .dropdown-label-color-preview.js-dropdown-label-color-preview
-                    .suggest-colors.suggest-colors-dropdown
-                      - suggested_colors.each do |color|
-                        = link_to '#', style: "background-color: #{color}", data: { color: color } do
-                          &nbsp
-                    %button.btn.btn-primary.js-new-label-btn{type: "button"}
-                      Create
-              = dropdown_loading
-              .dropdown-loading
-                = icon('spinner spin')
+          = render "shared/issuable/label_dropdown"
 
         .pull-right
           = render 'shared/sort_dropdown'
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index d5a4aad05d9f9824357219a47d1a0d0b4fc0cf6e..b01a36265f91d2d3fd091f1fbdd89b1815d9008e 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -13,12 +13,21 @@
 
     - if issuable.is_a?(MergeRequest)
       %p.help-block
-        - if issuable.work_in_progress?
-          Remove the <code>WIP</code> prefix from the title to allow this
-          <strong>Work In Progress</strong> merge request to be merged when it's ready.
-        - else
-          Start the title with <code>[WIP]</code> or <code>WIP:</code> to prevent a
-          <strong>Work In Progress</strong> merge request from being merged before it's ready.
+        .js-wip-explanation
+          %a.js-toggle-wip{href: ""}
+            Remove the
+            %code WIP:
+            prefix from the title
+          to allow this
+          %strong Work In Progress
+          merge request to be merged when it's ready.
+        .js-no-wip-explanation
+          %a.js-toggle-wip{href: ""}
+            Start the title with
+            %code WIP:
+          to prevent a
+          %strong Work In Progress
+          merge request from being merged before it's ready.
 .form-group.detail-page-description
   = f.label :description, 'Description', class: 'control-label'
   .col-sm-10
@@ -29,6 +38,15 @@
       = render 'projects/notes/hints'
       .clearfix
       .error-alert
+
+- if issuable.is_a?(Issue) && !issuable.project.private?
+  .form-group
+    .col-sm-offset-2.col-sm-10
+      .checkbox
+        = f.label :confidential do
+          = f.check_box :confidential
+          This issue is confidential and should only be visible to team members
+
 - if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project)
   %hr
   .form-group
@@ -67,13 +85,26 @@
       - if can? current_user, :admin_label, issuable.project
         = link_to 'Create new label', new_namespace_project_label_path(issuable.project.namespace, issuable.project), target: :blank
 
+- if issuable.can_move?(current_user)
+  %hr
+  .form-group
+    = label_tag :move_to_project_id, 'Move', class: 'control-label'
+    .col-sm-10
+      - projects = project_options(issuable, current_user, ability: :admin_issue)
+      = select_tag(:move_to_project_id, projects, include_blank: true,
+                   class: 'select2', data: { placeholder: 'Select project' })
+      &nbsp;
+      %span{ data: { toggle: 'tooltip', placement: 'auto top' }, style: 'cursor: default',
+      title: 'Moving an issue will copy the discussion to a different project and close it here. All participants will be notified of the new location.' }
+        = icon('question-circle')
+
 - if issuable.is_a?(MergeRequest)
   %hr
-    - if @merge_request.new_record?
-      .form-group
-        = f.label :source_branch, class: 'control-label'
-        .col-sm-10
-          = f.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true })
+  - if @merge_request.new_record?
+    .form-group
+      = f.label :source_branch, class: 'control-label'
+      .col-sm-10
+        = f.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true })
   .form-group
     = f.label :target_branch, class: 'control-label'
     .col-sm-10
@@ -96,7 +127,12 @@
       for this project.
 
   - if issuable.new_record?
-    - cancel_project = issuable.source_project
+    = link_to 'Cancel', namespace_project_issues_path(@project.namespace, @project), class: 'btn btn-cancel'
   - else
-    - cancel_project = issuable.project
-  = link_to 'Cancel', [cancel_project.namespace.becomes(Namespace), cancel_project, issuable], class: 'btn btn-cancel'
+    .pull-right
+      - if current_user.can?(:"destroy_#{issuable.to_ability_name}", @project)
+        = link_to polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), data: { confirm: "#{issuable.class.name.titleize} will be removed! Are you sure?" },
+                                                                                                  method: :delete, class: 'btn btn-grouped' do
+          = icon('trash-o')
+          Delete
+      = link_to 'Cancel', namespace_project_issue_path(@project.namespace, @project, issuable), class: 'btn btn-grouped btn-cancel'
diff --git a/app/views/shared/issuable/_label_dropdown.html.haml b/app/views/shared/issuable/_label_dropdown.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..876173151817caabf4ed92388f1a671d3f528856
--- /dev/null
+++ b/app/views/shared/issuable/_label_dropdown.html.haml
@@ -0,0 +1,39 @@
+- if params[:label_name]
+  = hidden_field_tag(:label_name, params[:label_name])
+.dropdown
+  %button.dropdown-menu-toggle.js-label-select.js-filter-submit{type: "button", data: {toggle: "dropdown", field_name: "label_name", show_no: "true", show_any: "true", selected: params[:label_name], project_id: @project.try(:id), labels: labels_filter_path, default_label: "Label"}}
+    %span.dropdown-toggle-text
+      = h(params[:label_name].presence || "Label")
+    = icon('chevron-down')
+  .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
+    .dropdown-page-one
+      = dropdown_title("Filter by label")
+      = dropdown_filter("Search labels")
+      = dropdown_content
+      - if @project
+        = dropdown_footer do
+          %ul.dropdown-footer-list
+            - if can? current_user, :admin_label, @project
+              %li
+                %a.dropdown-toggle-page{href: "#"}
+                  Create new
+            %li
+              = link_to namespace_project_labels_path(@project.namespace, @project) do
+                - if can? current_user, :admin_label, @project
+                  Manage labels
+                - else
+                  View labels
+    - if can? current_user, :admin_label, @project and @project
+      .dropdown-page-two
+        = dropdown_title("Create new label", back: true)
+        = dropdown_content do
+          %input#new_label_color{type: "hidden"}
+          %input#new_label_name.dropdown-input-field{type: "text", placeholder: "Name new label"}
+          .dropdown-label-color-preview.js-dropdown-label-color-preview
+          .suggest-colors.suggest-colors-dropdown
+            - suggested_colors.each do |color|
+              = link_to '#', style: "background-color: #{color}", data: { color: color } do
+                &nbsp
+          %button.btn.btn-primary.js-new-label-btn{type: "button"}
+            Create
+    = dropdown_loading
diff --git a/app/views/shared/issuable/_milestone_dropdown.html.haml b/app/views/shared/issuable/_milestone_dropdown.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..0434506c8d766ec602789a58ea9691bae255aa91
--- /dev/null
+++ b/app/views/shared/issuable/_milestone_dropdown.html.haml
@@ -0,0 +1,16 @@
+- if params[:milestone_title]
+  = hidden_field_tag(:milestone_title, params[:milestone_title])
+= dropdown_tag(h(params[:milestone_title].presence || "Milestone"), options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit', filter: true, dropdown_class: "dropdown-menu-selectable",
+  placeholder: "Search milestones", footer_content: @project.present?, data: { show_no: true, show_any: true, field_name: "milestone_title", selected: params[:milestone_title], project_id: @project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
+  - if @project
+    %ul.dropdown-footer-list
+      - if can? current_user, :admin_milestone, @project
+        %li
+          = link_to new_namespace_project_milestone_path(@project.namespace, @project), title: "New Milestone" do
+            Create new
+      %li
+        = link_to namespace_project_milestones_path(@project.namespace, @project) do
+          - if can? current_user, :admin_milestone, @project
+            Manage milestones
+          - else
+            View milestones
diff --git a/app/views/shared/issuable/_participants.html.haml b/app/views/shared/issuable/_participants.html.haml
index f1d92ef48b2f6cae0b15d7874f1e6b72c5cd7202..3fb409ff7279c843b4c6c00110542b3361144b03 100644
--- a/app/views/shared/issuable/_participants.html.haml
+++ b/app/views/shared/issuable/_participants.html.haml
@@ -1,3 +1,6 @@
+- participants_row = 7
+- participants_size = participants.size
+- participants_extra = participants_size - participants_row
 .block.participants
   .sidebar-collapsed-icon
     = icon('users')
@@ -5,6 +8,13 @@
       = participants.count
   .title.hide-collapsed
     = pluralize participants.count, "participant"
-  - participants.each do |participant|
-    %span.hide-collapsed
-      = link_to_member(@project, participant, name: false, size: 24)
+  .hide-collapsed.participants-list
+    - participants.each do |participant|
+      .participants-author.js-participants-author
+        = link_to_member(@project, participant, name: false, size: 24)
+    - if participants_extra > 0
+      %div.participants-more
+        %a.js-participants-more{href: "#", data: {original_text: "+ #{participants_size - 7} more", less_text: "- show less"}}
+          + #{participants_extra} more
+:javascript
+  Issue.prototype.PARTICIPANTS_ROW_COUNT = #{participants_row};
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 23b1ed1e51bca395ff0a4efec2ed0b033ac20db5..2b95b19facc640f39a520e9009583ca82ae23185 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -1,13 +1,12 @@
 %aside.right-sidebar{ class: sidebar_gutter_collapsed_class }
   .issuable-sidebar
-    .block
+    .block.issuable-sidebar-header
       %span.issuable-count.hide-collapsed.pull-left
         = issuable.iid
         of
         = issuables_count(issuable)
-      %span.pull-right
-        %a.gutter-toggle.js-sidebar-toggle{href: '#'}
-          = sidebar_gutter_toggle_icon
+      %a.gutter-toggle.pull-right.js-sidebar-toggle{href: '#'}
+        = sidebar_gutter_toggle_icon
       .issuable-nav.hide-collapsed.pull-right.btn-group{role: 'group', "aria-label" => '...'}
         - if prev_issuable = prev_issuable_for(issuable)
           = link_to 'Prev', [@project.namespace.becomes(Namespace), @project, prev_issuable], class: 'btn btn-default prev-btn'
@@ -22,20 +21,20 @@
 
     = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f|
       .block.assignee
-        .sidebar-collapsed-icon
+        .sidebar-collapsed-icon.sidebar-collapsed-user{data: {toggle: "tooltip", placement: "left", container: "body"}, title: (issuable.assignee.to_reference if issuable.assignee)}
           - if issuable.assignee
-            = link_to_member_avatar(issuable.assignee, size: 24)
+            = link_to_member(@project, issuable.assignee, size: 24)
           - else
             = icon('user')
         .title.hide-collapsed
-          %label
-            Assignee
+          Assignee
           - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
-            .pull-right
-              = link_to 'Edit', '#', class: 'edit-link'
-        .value.hide-collapsed
+            = link_to 'Edit', '#', class: 'edit-link pull-right'
+        .value.bold.hide-collapsed
           - if issuable.assignee
-            %strong= link_to_member(@project, issuable.assignee, size: 24)
+            = link_to_member(@project, issuable.assignee, size: 32) do
+              %span.username
+                = issuable.assignee.to_reference
             - if issuable.instance_of?(MergeRequest) && !issuable.can_be_merged_by?(issuable.assignee)
               %a.pull-right.cannot-be-merged{href: '#', data: {toggle: 'tooltip'}, title: 'Not allowed to merge'}
                 = icon('exclamation-triangle')
@@ -54,18 +53,13 @@
             - else
               No
         .title.hide-collapsed
-          %label
-            Milestone
+          Milestone
           - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
-            .pull-right
-              = link_to 'Edit', '#', class: 'edit-link'
-        .value.hide-collapsed
+            = link_to 'Edit', '#', class: 'edit-link pull-right'
+        .value.bold.hide-collapsed
           - if issuable.milestone
-            %span.back-to-milestone
-              = link_to namespace_project_milestone_path(@project.namespace, @project, issuable.milestone) do
-                %strong
-                  = icon('clock-o')
-                  = issuable.milestone.title
+            = link_to namespace_project_milestone_path(@project.namespace, @project, issuable.milestone) do
+              = issuable.milestone.title
           - else
             .light None
         .selectbox.hide-collapsed
@@ -80,11 +74,10 @@
             %span
               = issuable.labels.count
           .title.hide-collapsed
-            %label Labels
+            Labels
             - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
-              .pull-right
-                = link_to 'Edit', '#', class: 'edit-link'
-          .value.issuable-show-labels.hide-collapsed
+              = link_to 'Edit', '#', class: 'edit-link pull-right'
+          .value.issuable-show-labels.hide-collapsed{class: ("has-labels" if issuable.labels.any?)}
             - if issuable.labels.any?
               - issuable.labels.each do |label|
                 = link_to_label(label, type: issuable.to_ability_name)
@@ -95,14 +88,13 @@
               { selected: issuable.label_ids }, multiple: true, class: 'select2 js-select2', data: { placeholder: "Select labels" }
 
       = render "shared/issuable/participants", participants: issuable.participants(current_user)
-      %hr
       - if current_user
         - subscribed = issuable.subscribed?(current_user)
         .block.light.subscription{data: {url: toggle_subscription_path(issuable)}}
           .sidebar-collapsed-icon
             = icon('rss')
           .title.hide-collapsed
-            %label.light Notifications
+            Notifications
           - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed'
           %button.btn.btn-block.btn-gray.subscribe-button.hide-collapsed{:type => 'button'}
             %span= subscribed ? 'Unsubscribe' : 'Subscribe'
diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml
index f7c6fc14adf196b0ea186f01f6312e8800bb3a2c..e1127b2311cec445b4dbd61d76ed55678ce2a0d1 100644
--- a/app/views/shared/milestones/_issuable.html.haml
+++ b/app/views/shared/milestones/_issuable.html.haml
@@ -10,6 +10,8 @@
       %strong #{project.name} &middot;
     - elsif show_full_project_name
       %strong #{project.name_with_namespace} &middot;
+    - if issuable.is_a?(Issue)
+      = confidential_icon(issuable)
     = link_to_gfm issuable.title, [project.namespace.becomes(Namespace), project, issuable], title: issuable.title
   %div{class: 'issuable-detail'}
     = link_to [project.namespace.becomes(Namespace), project, issuable] do
@@ -21,5 +23,5 @@
 
     - if assignee
       = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }),
-                class: 'has_tooltip', data: { 'original-title' => "Assigned to #{sanitize(assignee.name)}", container: 'body' } do
+                class: 'has-tooltip', data: { 'original-title' => "Assigned to #{sanitize(assignee.name)}", container: 'body' } do
         - image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '')
diff --git a/app/views/shared/milestones/_labels_tab.html.haml b/app/views/shared/milestones/_labels_tab.html.haml
index ba27bafd1bc0ce870843a609f4c0bb4cb003d10b..868b2357003e8b14da5701cd5e3dd3b2378ebb61 100644
--- a/app/views/shared/milestones/_labels_tab.html.haml
+++ b/app/views/shared/milestones/_labels_tab.html.haml
@@ -5,7 +5,7 @@
     %li
       %span.label-row
         = link_to milestones_label_path(options) do
-          - render_colored_label(label)
+          - render_colored_label(label, tooltip: false)
         %span.prepend-left-10
           = markdown(label.description, pipeline: :single_line)
 
diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml
index f01138af3f09c20d2eaef18d63366c44d733aa9f..6b25745c55420f400ff8605739b345d8e5547e00 100644
--- a/app/views/shared/milestones/_milestone.html.haml
+++ b/app/views/shared/milestones/_milestone.html.haml
@@ -6,10 +6,10 @@
     .col-sm-6
       %strong= link_to_gfm truncate(milestone.title, length: 100), milestone_path
     .col-sm-6
-      .pull-right.light #{milestone.percent_complete}% complete
+      .pull-right.light #{milestone.percent_complete(current_user)}% complete
   .row
     .col-sm-6
-      = link_to pluralize(milestone.issues.size, 'Issue'), issues_path
+      = link_to pluralize(milestone.issues_visible_to_user(current_user).size, 'Issue'), issues_path
       &middot;
       = link_to pluralize(milestone.merge_requests.size, 'Merge Request'), merge_requests_path
     .col-sm-6= milestone_progress_bar(milestone)
diff --git a/app/views/shared/milestones/_summary.html.haml b/app/views/shared/milestones/_summary.html.haml
index 59d4ae29f791f349d13e0bc8903011c5655f3636..385c65966067485788a8bc4b3c44d164e4b3c083 100644
--- a/app/views/shared/milestones/_summary.html.haml
+++ b/app/views/shared/milestones/_summary.html.haml
@@ -3,15 +3,15 @@
 .context.prepend-top-default
   .milestone-summary
     %h4 Progress
-    %strong= milestone.issues.size
+    %strong= milestone.issues_visible_to_user(current_user).size
     issues:
     %span.milestone-stat
-      %strong= milestone.issues.opened.size
+      %strong= milestone.issues_visible_to_user(current_user).opened.size
       open and
-      %strong= milestone.issues.closed.size
+      %strong= milestone.issues_visible_to_user(current_user).closed.size
       closed
     %span.milestone-stat
-      %strong== #{milestone.percent_complete}%
+      %strong== #{milestone.percent_complete(current_user)}%
       complete
 
     %span.milestone-stat
diff --git a/app/views/shared/milestones/_tabs.html.haml b/app/views/shared/milestones/_tabs.html.haml
index 57d7ee85a3b3ac27289d39c02e5b5ce21a9c3c31..2b6ce2d7e7a38cf6304802b540d5f8dbb32745c1 100644
--- a/app/views/shared/milestones/_tabs.html.haml
+++ b/app/views/shared/milestones/_tabs.html.haml
@@ -2,7 +2,7 @@
   %li.active
     = link_to '#tab-issues', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do
       Issues
-      %span.badge= milestone.issues.size
+      %span.badge= milestone.issues_visible_to_user(current_user).size
   %li
     = link_to '#tab-merge-requests', 'data-toggle' => 'tab', 'data-show' => '.tab-merge-requests-buttons' do
       Merge Requests
@@ -21,7 +21,7 @@
 
 .tab-content.milestone-content
   .tab-pane.active#tab-issues
-    = render 'shared/milestones/issues_tab', issues: milestone.issues, show_project_name: show_project_name, show_full_project_name: show_full_project_name
+    = render 'shared/milestones/issues_tab', issues: milestone.issues_visible_to_user(current_user), show_project_name: show_project_name, show_full_project_name: show_full_project_name
   .tab-pane#tab-merge-requests
     = render 'shared/milestones/merge_requests_tab', merge_requests: milestone.merge_requests, show_project_name: show_project_name, show_full_project_name: show_full_project_name
   .tab-pane#tab-participants
diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml
index 4cf1d948b5b952d84f08931b556433b79f0b9e4b..cab8743a0772dca5e169fe3d4b3fbc16ab993154 100644
--- a/app/views/shared/milestones/_top.html.haml
+++ b/app/views/shared/milestones/_top.html.haml
@@ -28,7 +28,7 @@
   %h2.title
     = markdown escape_once(milestone.title), pipeline: :single_line
 
-- if milestone.complete? && milestone.active?
+- if milestone.complete?(current_user) && milestone.active?
   .alert.alert-success.prepend-top-default
     - close_msg = group ? 'You may close the milestone now.' : 'Navigate to the project to close the milestone.'
     %span All issues for this milestone are closed. #{close_msg}
@@ -47,7 +47,7 @@
           - project_name = group ? ms.project.name : ms.project.name_with_namespace
           = link_to project_name, namespace_project_milestone_path(ms.project.namespace, ms.project, ms)
         %td
-          = ms.issues.opened.count
+          = ms.issues_visible_to_user(current_user).opened.count
         %td
           - if ms.closed?
             Closed
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index 97cfb76cdb0da0337d2b6e8d79d4439531d18b9b..803dd95bc65ff8ae70a5bf3c0d07aca85809f472 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -7,26 +7,11 @@
 - show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true && project.commit
 - css_class += " no-description" if project.description.blank? && !show_last_commit_as_description
 - ci_commit = project.ci_commit(project.commit.sha) if ci && !project.empty_repo? && project.commit
-- cache_key = [project.namespace, project, controller.controller_name, controller.action_name, current_application_settings, 'v2.2']
+- cache_key = [project.namespace, project, controller.controller_name, controller.action_name, current_application_settings, 'v2.3']
 - cache_key.push(ci_commit.status) if ci_commit
 
 %li.project-row{ class: css_class }
   = cache(cache_key) do
-    = link_to project_path(project), class: dom_class(project) do
-      - if avatar
-        .dash-project-avatar
-          - if use_creator_avatar
-            = image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:''
-          - else
-            = project_icon(project, alt: '', class: 'avatar project-avatar s40')
-      %span.project-full-name.title
-        %span.namespace-name
-          - if project.namespace && !skip_namespace
-            = project.namespace.human_name
-            \/
-        %span.project-name.filter-title
-          = project.name
-
     .controls
       - if project.main_language
         %span
@@ -42,9 +27,25 @@
         %span
           = icon('star')
           = project.star_count
-      %span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' },
-        title: "#{visibility_level_label(project.visibility_level)} - #{project_visibility_level_description(project.visibility_level)}"}
+      %span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(project)}
         = visibility_level_icon(project.visibility_level, fw: false)
+
+    .title
+      = link_to project_path(project), class: dom_class(project) do
+        - if avatar
+          .dash-project-avatar
+            - if use_creator_avatar
+              = image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:''
+            - else
+              = project_icon(project, alt: '', class: 'avatar project-avatar s40')
+        %span.project-full-name
+          %span.namespace-name
+            - if project.namespace && !skip_namespace
+              = project.namespace.human_name
+              \/
+          %span.project-name.filter-title
+            = project.name
+
     - if show_last_commit_as_description
       .description
         = link_to_gfm project.commit.title, namespace_project_commit_path(project.namespace, project, project.commit),
diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml
index aa5acee9c14c1e495826bdb6a9c0a58637691347..3c445f672368b13c54cebcb9f6f7c1703dd9a893 100644
--- a/app/views/shared/snippets/_header.html.haml
+++ b/app/views/shared/snippets/_header.html.haml
@@ -1,5 +1,5 @@
 .detail-page-header
-  .snippet-box.has_tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: 'body' }}
+  .snippet-box.has-tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: 'body' }}
     = visibility_level_icon(@snippet.visibility_level, fw: false)
     = visibility_level_label(@snippet.visibility_level)
   %span.identifier
diff --git a/app/views/shared/snippets/_snippet.html.haml b/app/views/shared/snippets/_snippet.html.haml
index a316a085107e2edc6f0f7b4ef7b075ce5b16b313..c96dfefe17f5d21e0634714c9cfdf2e35687aab5 100644
--- a/app/views/shared/snippets/_snippet.html.haml
+++ b/app/views/shared/snippets/_snippet.html.haml
@@ -1,8 +1,8 @@
 %li.snippet-row
   = image_tag avatar_icon(snippet.author_email), class: "avatar s40 hidden-xs", alt: ''
 
-  .snippet-title
-    = link_to reliable_snippet_path(snippet), class: 'title' do
+  .title
+    = link_to reliable_snippet_path(snippet) do
       = truncate(snippet.title, length: 60)
       - if snippet.private?
         %span.label.label-gray
diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml
index 20d2d5f317bdfd2eed538e681d905b88056b80a2..02647229776455a13178d43a108b9b71beec4c50 100644
--- a/app/views/votes/_votes_block.html.haml
+++ b/app/views/votes/_votes_block.html.haml
@@ -1,6 +1,6 @@
 .awards.votes-block
   - awards_sort(votable.notes.awards.grouped_awards).each do |emoji, notes|
-    %button.btn.award-control.js-emoji-btn.has_tooltip{class: (note_active_class(notes, current_user)), title: emoji_author_list(notes, current_user), data: {placement: "top"}}
+    %button.btn.award-control.js-emoji-btn.has-tooltip{class: (note_active_class(notes, current_user)), title: emoji_author_list(notes, current_user), data: {placement: "top"}}
       = emoji_icon(emoji)
       %span.award-control-text.js-counter
         = notes.count
diff --git a/app/workers/gitlab_shell_one_shot_worker.rb b/app/workers/gitlab_shell_one_shot_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4ddbcf574d5eba6057fd53229709387a9220affc
--- /dev/null
+++ b/app/workers/gitlab_shell_one_shot_worker.rb
@@ -0,0 +1,10 @@
+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/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb
index 21d311579e34af22f636a9c3f2d35e05cd4bd850..f9e32337983943a1abc2ce2cf624aea1843d55fe 100644
--- a/app/workers/repository_fork_worker.rb
+++ b/app/workers/repository_fork_worker.rb
@@ -20,14 +20,15 @@ class RepositoryForkWorker
       return
     end
 
+    project.repository.after_import
+
     unless project.valid_repo?
-      logger.error("Project #{id} had an invalid repository after fork")
+      logger.error("Project #{project_id} had an invalid repository after fork")
       project.update(import_error: "The forked repository is invalid.")
       project.import_fail
       return
     end
 
-    project.repository.after_import
     project.import_finish
   end
 end
diff --git a/config/application.rb b/config/application.rb
index 2b103c4592db16102d46a86d586b246de753c473..5a0ac70aa2aabcd46252f149ba0098d4726920ef 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -49,6 +49,7 @@ module Gitlab
     config.assets.paths << Gemojione.index.images_path
     config.assets.precompile << "*.png"
     config.assets.precompile << "print.css"
+    config.assets.precompile << "notify.css"
 
     # Version of your assets, change this if you want to expire all your assets
     config.assets.version = '1.0'
diff --git a/config/initializers/premailer.rb b/config/initializers/premailer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a44316bc3a429e846dde300605ec79cecaf61427
--- /dev/null
+++ b/config/initializers/premailer.rb
@@ -0,0 +1,7 @@
+# See https://github.com/fphilipe/premailer-rails#configuration
+Premailer::Rails.config.merge!(
+  generate_text_part: false,
+  preserve_styles: true,
+  remove_comments: true,
+  remove_ids: true
+)
diff --git a/config/routes.rb b/config/routes.rb
index 2ae282f48a6db81779fef960597f4eddd7b50548..90d858d7fc101a671b6b5605905297a5d16c62af 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -351,6 +351,8 @@ Rails.application.routes.draw do
     get :issues
     get :merge_requests
     get :activity
+    get :labels
+    get :milestones
 
     scope module: :dashboard do
       resources :milestones, only: [:index, :show]
@@ -611,7 +613,7 @@ Rails.application.routes.draw do
           end
         end
 
-        resources :merge_requests, constraints: { id: /\d+/ }, except: [:destroy] do
+        resources :merge_requests, constraints: { id: /\d+/ } do
           member do
             get :commits
             get :diffs
@@ -621,6 +623,7 @@ Rails.application.routes.draw do
             post :cancel_merge_when_build_succeeds
             get :ci_status
             post :toggle_subscription
+            post :remove_wip
           end
 
           collection do
@@ -681,7 +684,7 @@ Rails.application.routes.draw do
           end
         end
 
-        resources :issues, constraints: { id: /\d+/ }, except: [:destroy] do
+        resources :issues, constraints: { id: /\d+/ } do
           member do
             post :toggle_subscription
           end
diff --git a/db/migrate/20160223192159_add_confidential_to_issues.rb b/db/migrate/20160223192159_add_confidential_to_issues.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e9d47fd589aff45f4de9c10db52b066074e05131
--- /dev/null
+++ b/db/migrate/20160223192159_add_confidential_to_issues.rb
@@ -0,0 +1,6 @@
+class AddConfidentialToIssues < ActiveRecord::Migration
+  def change
+    add_column :issues, :confidential, :boolean, default: false
+    add_index :issues, :confidential
+  end
+end
diff --git a/db/migrate/20160225090018_add_delete_at_to_issues.rb b/db/migrate/20160225090018_add_delete_at_to_issues.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3ddbef92978bcad88a87324f5c1fece3e0bf537c
--- /dev/null
+++ b/db/migrate/20160225090018_add_delete_at_to_issues.rb
@@ -0,0 +1,6 @@
+class AddDeleteAtToIssues < ActiveRecord::Migration
+  def change
+    add_column :issues, :deleted_at, :datetime
+    add_index :issues, :deleted_at
+  end
+end
diff --git a/db/migrate/20160225101956_add_delete_at_to_merge_requests.rb b/db/migrate/20160225101956_add_delete_at_to_merge_requests.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9d09105f17db368ac75750a2c730ba2b2d9e7aa3
--- /dev/null
+++ b/db/migrate/20160225101956_add_delete_at_to_merge_requests.rb
@@ -0,0 +1,6 @@
+class AddDeleteAtToMergeRequests < ActiveRecord::Migration
+  def change
+    add_column :merge_requests, :deleted_at, :datetime
+    add_index :merge_requests, :deleted_at
+  end
+end
diff --git a/db/migrate/20160301124843_add_visibility_level_to_groups.rb b/db/migrate/20160301124843_add_visibility_level_to_groups.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d1b921bb208e897aa23322e942d7fc3571433438
--- /dev/null
+++ b/db/migrate/20160301124843_add_visibility_level_to_groups.rb
@@ -0,0 +1,29 @@
+class AddVisibilityLevelToGroups < ActiveRecord::Migration
+  def up
+    add_column :namespaces, :visibility_level, :integer, null: false, default: Gitlab::VisibilityLevel::PUBLIC
+    add_index :namespaces, :visibility_level
+
+    # Unfortunately, this is needed on top of the `default`, since we don't want the configuration specific
+    # `allowed_visibility_level` to end up in schema.rb
+    if allowed_visibility_level < Gitlab::VisibilityLevel::PUBLIC
+      execute("UPDATE namespaces SET visibility_level = #{allowed_visibility_level}")
+    end
+  end
+
+  def down
+    remove_column :namespaces, :visibility_level
+  end
+
+  private
+
+  def allowed_visibility_level
+    application_settings = select_one("SELECT restricted_visibility_levels FROM application_settings ORDER BY id DESC LIMIT 1")
+    if application_settings
+      restricted_visibility_levels = YAML.safe_load(application_settings["restricted_visibility_levels"]) rescue nil
+    end
+    restricted_visibility_levels ||= []
+
+    allowed_levels = Gitlab::VisibilityLevel.values - restricted_visibility_levels
+    allowed_levels.max
+  end
+end
diff --git a/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb b/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb
new file mode 100644
index 0000000000000000000000000000000000000000..75de5f70fa299e93f2dfe369610f505d6e878fdc
--- /dev/null
+++ b/db/migrate/20160308212903_add_default_group_visibility_to_application_settings.rb
@@ -0,0 +1,29 @@
+# Create visibility level field on DB
+# Sets default_visibility_level to value on settings if not restricted
+# If value is restricted takes higher visibility level allowed
+
+class AddDefaultGroupVisibilityToApplicationSettings < ActiveRecord::Migration
+  def up
+    add_column :application_settings, :default_group_visibility, :integer
+    # Unfortunately, this can't be a `default`, since we don't want the configuration specific
+    # `allowed_visibility_level` to end up in schema.rb
+    execute("UPDATE application_settings SET default_group_visibility = #{allowed_visibility_level}")
+  end
+
+  def down
+    remove_column :application_settings, :default_group_visibility
+  end
+
+  private
+
+  def allowed_visibility_level
+    application_settings = select_one("SELECT restricted_visibility_levels FROM application_settings ORDER BY id DESC LIMIT 1")
+    if application_settings
+      restricted_visibility_levels = YAML.safe_load(application_settings["restricted_visibility_levels"]) rescue nil
+    end
+    restricted_visibility_levels ||= []
+
+    allowed_levels = Gitlab::VisibilityLevel.values - restricted_visibility_levels
+    allowed_levels.max
+  end
+end
diff --git a/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb b/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6871b3920df145b0d609cddb8b7ba8dc881f0851
--- /dev/null
+++ b/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb
@@ -0,0 +1,5 @@
+class ChangeTargetIdToNullOnTodos < ActiveRecord::Migration
+  def change
+    change_column_null :todos, :target_id, true
+  end
+end
diff --git a/db/migrate/20160316204731_add_commit_id_to_todos.rb b/db/migrate/20160316204731_add_commit_id_to_todos.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ae19fdd1abd3c525a4955a527ec2b89215e285f3
--- /dev/null
+++ b/db/migrate/20160316204731_add_commit_id_to_todos.rb
@@ -0,0 +1,6 @@
+class AddCommitIdToTodos < ActiveRecord::Migration
+  def change
+    add_column :todos, :commit_id, :string
+    add_index :todos, :commit_id
+  end
+end
diff --git a/db/migrate/20160317092222_add_moved_to_to_issue.rb b/db/migrate/20160317092222_add_moved_to_to_issue.rb
new file mode 100644
index 0000000000000000000000000000000000000000..461e7fb3a9bdf3d0e30879290bd02d777bfc0067
--- /dev/null
+++ b/db/migrate/20160317092222_add_moved_to_to_issue.rb
@@ -0,0 +1,5 @@
+class AddMovedToToIssue < ActiveRecord::Migration
+  def change
+    add_reference :issues, :moved_to, references: :issues
+  end
+end
diff --git a/db/migrate/20160320204112_index_namespaces_on_visibility_level.rb b/db/migrate/20160320204112_index_namespaces_on_visibility_level.rb
new file mode 100644
index 0000000000000000000000000000000000000000..370b339d45c7ace3c748cbb868e58e4c0a54d9f5
--- /dev/null
+++ b/db/migrate/20160320204112_index_namespaces_on_visibility_level.rb
@@ -0,0 +1,7 @@
+class IndexNamespacesOnVisibilityLevel < ActiveRecord::Migration
+  def change
+    unless index_exists?(:namespaces, :visibility_level)
+      add_index :namespaces, :visibility_level
+    end
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 2f075677b308e46dc586eb5416809706db41f1be..dce2bfe62cab83e5d1881ef2fbd05fbae7a3b82f 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: 20160316123110) do
+ActiveRecord::Schema.define(version: 20160320204112) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -77,6 +77,7 @@ ActiveRecord::Schema.define(version: 20160316123110) do
     t.boolean  "akismet_enabled",                   default: false
     t.string   "akismet_api_key"
     t.boolean  "email_author_in_body",              default: false
+    t.integer  "default_group_visibility"
   end
 
   create_table "audit_events", force: :cascade do |t|
@@ -416,12 +417,17 @@ ActiveRecord::Schema.define(version: 20160316123110) do
     t.string   "state"
     t.integer  "iid"
     t.integer  "updated_by_id"
+    t.integer  "moved_to_id"
+    t.boolean  "confidential",              default: false
+    t.datetime "deleted_at"
   end
 
   add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree
   add_index "issues", ["author_id"], name: "index_issues_on_author_id", using: :btree
+  add_index "issues", ["confidential"], name: "index_issues_on_confidential", using: :btree
   add_index "issues", ["created_at", "id"], name: "index_issues_on_created_at_and_id", using: :btree
   add_index "issues", ["created_at"], name: "index_issues_on_created_at", using: :btree
+  add_index "issues", ["deleted_at"], name: "index_issues_on_deleted_at", using: :btree
   add_index "issues", ["description"], name: "index_issues_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
   add_index "issues", ["milestone_id"], name: "index_issues_on_milestone_id", using: :btree
   add_index "issues", ["project_id", "iid"], name: "index_issues_on_project_id_and_iid", unique: true, using: :btree
@@ -544,12 +550,14 @@ ActiveRecord::Schema.define(version: 20160316123110) do
     t.boolean  "merge_when_build_succeeds", default: false, null: false
     t.integer  "merge_user_id"
     t.string   "merge_commit_sha"
+    t.datetime "deleted_at"
   end
 
   add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
   add_index "merge_requests", ["author_id"], name: "index_merge_requests_on_author_id", using: :btree
   add_index "merge_requests", ["created_at", "id"], name: "index_merge_requests_on_created_at_and_id", using: :btree
   add_index "merge_requests", ["created_at"], name: "index_merge_requests_on_created_at", using: :btree
+  add_index "merge_requests", ["deleted_at"], name: "index_merge_requests_on_deleted_at", using: :btree
   add_index "merge_requests", ["description"], name: "index_merge_requests_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
   add_index "merge_requests", ["milestone_id"], name: "index_merge_requests_on_milestone_id", using: :btree
   add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree
@@ -588,6 +596,7 @@ ActiveRecord::Schema.define(version: 20160316123110) do
     t.string   "description",           default: "",    null: false
     t.string   "avatar"
     t.boolean  "share_with_group_lock", default: false
+    t.integer  "visibility_level",      default: 20,    null: false
   end
 
   add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree
@@ -597,6 +606,7 @@ ActiveRecord::Schema.define(version: 20160316123110) do
   add_index "namespaces", ["path"], name: "index_namespaces_on_path", unique: true, using: :btree
   add_index "namespaces", ["path"], name: "index_namespaces_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"}
   add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree
+  add_index "namespaces", ["visibility_level"], name: "index_namespaces_on_visibility_level", using: :btree
 
   create_table "notes", force: :cascade do |t|
     t.text     "note"
@@ -865,7 +875,7 @@ ActiveRecord::Schema.define(version: 20160316123110) do
   create_table "todos", force: :cascade do |t|
     t.integer  "user_id",     null: false
     t.integer  "project_id",  null: false
-    t.integer  "target_id",   null: false
+    t.integer  "target_id"
     t.string   "target_type", null: false
     t.integer  "author_id"
     t.integer  "action",      null: false
@@ -873,9 +883,11 @@ ActiveRecord::Schema.define(version: 20160316123110) do
     t.datetime "created_at"
     t.datetime "updated_at"
     t.integer  "note_id"
+    t.string   "commit_id"
   end
 
   add_index "todos", ["author_id"], name: "index_todos_on_author_id", using: :btree
+  add_index "todos", ["commit_id"], name: "index_todos_on_commit_id", using: :btree
   add_index "todos", ["note_id"], name: "index_todos_on_note_id", using: :btree
   add_index "todos", ["project_id"], name: "index_todos_on_project_id", using: :btree
   add_index "todos", ["state"], name: "index_todos_on_state", using: :btree
diff --git a/doc/README.md b/doc/README.md
index db19c3de8d1dd2a00f8b0929320a005b1279c785..08d0a6a5bfbd958d8e477442298360e93c9edca3 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -3,6 +3,7 @@
 ## User documentation
 
 - [API](api/README.md) Automate GitLab via a simple and powerful API.
+- [CI](ci/README.md)
 - [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
 - [GitLab Basics](gitlab-basics/README.md) Find step by step how to start working on your commandline and on GitLab.
 - [Importing to GitLab](workflow/importing/README.md).
@@ -16,42 +17,6 @@
 - [Webhooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
 - [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
 
-## CI User documentation
-
-- [Get started with GitLab CI](ci/quick_start/README.md)
-- [Learn how to enable or disable GitLab CI](ci/enable_or_disable_ci.md)
-- [Learn how `.gitlab-ci.yml` works](ci/yaml/README.md)
-- [Configure a Runner, the application that runs your builds](ci/runners/README.md)
-- [Use Docker images with GitLab Runner](ci/docker/using_docker_images.md)
-- [Use CI to build Docker images](ci/docker/using_docker_build.md)
-- [Use variables in your `.gitlab-ci.yml`](ci/variables/README.md)
-- [Use SSH keys in your build environment](ci/ssh_keys/README.md)
-- [Trigger builds through the API](ci/triggers/README.md)
-- [Build artifacts](ci/build_artifacts/README.md)
-- [User permissions](ci/permissions/README.md)
-- [API](ci/api/README.md)
-
-### CI Examples
-
-- [The .gitlab-ci.yml file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml)
-- [Test your PHP applications](ci/examples/php.md)
-- [Test and deploy Ruby applications to Heroku](ci/examples/test-and-deploy-ruby-application-to-heroku.md)
-- [Test and deploy Python applications to Heroku](ci/examples/test-and-deploy-python-application-to-heroku.md)
-- [Test Clojure applications](ci/examples/test-clojure-application.md)
-- [Using `dpl` as deployment tool](ci/deployment/README.md)
-- Help your favorite programming language and GitLab by sending a merge request
-  with a guide for that language.
-
-### CI Services
-
-GitLab CI uses the `services` keyword to define what docker containers should
-be linked with your base image. Below is a list of examples you may use:
-
-- [Using MySQL](ci/services/mysql.md)
-- [Using PostgreSQL](ci/services/postgres.md)
-- [Using Redis](ci/services/redis.md)
-- [Using Other Services](ci/docker/using_docker_images.md#how-to-use-other-images-as-services)
-
 ## Administrator documentation
 
 - [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when webhooks aren't enough.
diff --git a/doc/api/groups.md b/doc/api/groups.md
index d47e79ba47f20aa52d2f3e987ea3f5e24245df5a..d1b5c9f5f048f7de844f817335123291b2da97e2 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -111,6 +111,7 @@ Parameters:
 - `name` (required) - The name of the group
 - `path` (required) - The path of the group
 - `description` (optional) - The group's description
+- `visibility_level` (optional) - The group's visibility. 0 for private, 10 for internal, 20 for public.
 
 ## Transfer project to group
 
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 9e704648b25e989f1b1708d3d45e2cad68fa4889..18d64c41986a7292fe0d0eb41ed348e2a98b4f9d 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -326,17 +326,25 @@ Example response:
 }
 ```
 
-## Delete existing issue (**Deprecated**)
+## Delete an issue
 
-This call is deprecated and returns a `405 Method Not Allowed` error if called.
-An issue gets now closed and is done by calling
-`PUT /projects/:id/issues/:issue_id` with the parameter `state_event` set to
-`close`. See [edit issue](#edit-issue) for more details.
+Only for admins and project owners. Soft deletes the issue in question.
+If the operation is successful, a status code `200` is returned. In case you cannot
+destroy this issue, or it is not present, code `404` is given.
 
 ```
 DELETE /projects/:id/issues/:issue_id
 ```
 
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id`            | integer | yes | The ID of a project |
+| `issue_id`      | integer | yes | The ID of a project's issue |
+
+```bash
+curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/issues/85
+```
+
 ## Comments on issues
 
 Comments are done via the [notes](notes.md) resource.
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 5c527d55481d2f85838a64c45b0d27030f22994f..b20a6300b7ad9aefe0a8bedcf927dfd3d1a900da 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -380,6 +380,25 @@ Parameters:
 If the operation is successful, 200 and the updated merge request is returned.
 If an error occurs, an error number and a message explaining the reason is returned.
 
+## Delete a merge request
+
+Only for admins and project owners. Soft deletes the merge request in question.
+If the operation is successful, a status code `200` is returned. In case you cannot
+destroy this merge request, or it is not present, code `404` is given.
+
+```
+DELETE /projects/:id/merge_requests/:merge_request_id
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id`            | integer | yes | The ID of a project |
+| `merge_request_id` | integer | yes | The ID of a project's merge request |
+
+```bash
+curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/merge_request/85
+```
+
 ## Accept MR
 
 Merge changes submitted with MR using this API.
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index a9b79bbdb1b800fa8fca23f657a67fb51d65e523..4316f3c1f6449f0e4bc33109777cfd0fb676210e 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -279,6 +279,8 @@ job_name:
 | Keyword       | Required | Description |
 |---------------|----------|-------------|
 | script        | yes | Defines a shell script which is executed by runner |
+| image         | no | Use docker image, covered in [Using Docker Images](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml) |
+| services      | no | Use docker services, covered in [Using Docker Images](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml) |
 | stage         | no | Defines a build stage (default: `test`) |
 | type          | no | Alias for `stage` |
 | only          | no | Defines a list of git refs for which build is created |
@@ -329,7 +331,7 @@ There are a few rules that apply to the usage of refs policy:
 * `only` and `except` are inclusive. If both `only` and `except` are defined
    in a job specification, the ref is filtered by `only` and `except`.
 * `only` and `except` allow the use of regular expressions.
-* `only` and `except` allow the use of special keywords: `branches` and `tags`.
+* `only` and `except` allow the use of special keywords: `branches`, `tags`, and `triggers`.
 * `only` and `except` allow to specify a repository path to filter jobs for
    forks.
 
@@ -346,6 +348,17 @@ job:
     - branches
 ```
 
+In this example, `job` will run only for refs that are tagged, or if a build is explicitly requested
+via an API trigger.
+
+```yaml
+job:
+  # use special keywords
+  only:
+    - tags
+    - triggers
+```
+
 The repository path can be used to have jobs executed only for the parent
 repository and not forks:
 
diff --git a/doc/gitlab-basics/README.md b/doc/gitlab-basics/README.md
index 493e1d1b09c1b5a1fe0e188343630b4d705f028a..3aa83975ace88d00953bc3445ea2c0257a8ea0e6 100644
--- a/doc/gitlab-basics/README.md
+++ b/doc/gitlab-basics/README.md
@@ -2,26 +2,14 @@
 
 Step-by-step guides on the basics of working with Git and GitLab.
 
-* [Start using Git on the command line](start-using-git.md)
-
-* [Create and add your SSH Keys](create-your-ssh-keys.md)
-
-* [Command Line basic commands](command-line-commands.md)
-
-* [Basic Git commands](basic-git-commands.md)
-
-* [Create a project](create-project.md)
-
-* [Create a group](create-group.md)
-
-* [Create a branch](create-branch.md)
-
-* [Fork a project](fork-project.md)
-
-* [Add a file](add-file.md)
-
-* [Add an image](add-image.md)
-
-* [Create a Merge Request](add-merge-request.md)
-
-* [Create an Issue](create-issue.md)
+- [Start using Git on the command line](start-using-git.md)
+- [Create and add your SSH Keys](create-your-ssh-keys.md)
+- [Command Line basics](command-line-commands.md)
+- [Create a project](create-project.md)
+- [Create a group](create-group.md)
+- [Create a branch](create-branch.md)
+- [Fork a project](fork-project.md)
+- [Add a file](add-file.md)
+- [Add an image](add-image.md)
+- [Create a Merge Request](add-merge-request.md)
+- [Create an Issue](create-issue.md)
diff --git a/doc/gitlab-basics/basic-git-commands.md b/doc/gitlab-basics/basic-git-commands.md
index 2b5767dd2d360dc1230779834c203d6aa2952432..c2a3415cbc4ef709256ed634ec16b027ad076833 100644
--- a/doc/gitlab-basics/basic-git-commands.md
+++ b/doc/gitlab-basics/basic-git-commands.md
@@ -1,59 +1,3 @@
 # Basic Git commands
 
-### Go to the master branch to pull the latest changes from there
-```
-git checkout master
-```
-
-### Download the latest changes in the project
-This is for you to work on an up-to-date copy (it is important to do every time you work on a project), while you setup tracking branches.
-```
-git pull REMOTE NAME-OF-BRANCH -u
-```
-(REMOTE: origin) (NAME-OF-BRANCH: could be "master" or an existing branch)
-
-### Create a branch
-Spaces won't be recognized, so you need to use a hyphen or underscore.
-```
-git checkout -b NAME-OF-BRANCH
-```
-
-### Work on a branch that has already been created
-```
-git checkout NAME-OF-BRANCH
-```
-
-### View the changes you've made
-It's important to be aware of what's happening and what's the status of your changes.
-```
-git status
-```
-
-### Add changes to commit
-You'll see your changes in red when you type "git status".
-```
-git add CHANGES IN RED
-git commit -m "DESCRIBE THE INTENTION OF THE COMMIT"
-```
-
-### Send changes to gitlab.com
-```
-git push REMOTE NAME-OF-BRANCH
-```
-
-### Delete all changes in the Git repository, but leave unstaged things
-```
-git checkout .
-```
-
-### Delete all changes in the Git repository, including untracked files
-```
-git clean -f
-```
-
-### Merge created branch with master branch
-You need to be in the created branch.
-```
-git checkout NAME-OF-BRANCH
-git merge master
-```
+This section is now merged into [Start using Git](start-using-git.md).
diff --git a/doc/gitlab-basics/start-using-git.md b/doc/gitlab-basics/start-using-git.md
index b2ceda025c0887481b9f97d46c786d75f6074b2f..89ce8bcc3e88b217cb18362942f036da66fa9bf2 100644
--- a/doc/gitlab-basics/start-using-git.md
+++ b/doc/gitlab-basics/start-using-git.md
@@ -1,6 +1,7 @@
 # Start using Git on the command line
 
-If you want to start using a Git and GitLab, make sure that you have created an account on GitLab.
+If you want to start using a Git and GitLab, make sure that you have created an
+account on GitLab.
 
 ## Open a shell
 
@@ -59,3 +60,63 @@ To view the information that you entered, type:
 ```
 git config --global --list
 ```
+## Basic Git commands
+
+### Go to the master branch to pull the latest changes from there
+
+```
+git checkout master
+```
+
+### Download the latest changes in the project
+This is for you to work on an up-to-date copy (it is important to do every time you work on a project), while you setup tracking branches.
+```
+git pull REMOTE NAME-OF-BRANCH -u
+```
+(REMOTE: origin) (NAME-OF-BRANCH: could be "master" or an existing branch)
+
+### Create a branch
+Spaces won't be recognized, so you need to use a hyphen or underscore.
+```
+git checkout -b NAME-OF-BRANCH
+```
+
+### Work on a branch that has already been created
+```
+git checkout NAME-OF-BRANCH
+```
+
+### View the changes you've made
+It's important to be aware of what's happening and what's the status of your changes.
+```
+git status
+```
+
+### Add changes to commit
+You'll see your changes in red when you type "git status".
+```
+git add CHANGES IN RED
+git commit -m "DESCRIBE THE INTENTION OF THE COMMIT"
+```
+
+### Send changes to gitlab.com
+```
+git push REMOTE NAME-OF-BRANCH
+```
+
+### Delete all changes in the Git repository, but leave unstaged things
+```
+git checkout .
+```
+
+### Delete all changes in the Git repository, including untracked files
+```
+git clean -f
+```
+
+### Merge created branch with master branch
+You need to be in the created branch.
+```
+git checkout NAME-OF-BRANCH
+git merge master
+```
diff --git a/doc/hooks/custom_hooks.md b/doc/hooks/custom_hooks.md
index 15051dd76f97abaf65dd5c083ee63cb7e40872cf..dcdf49d3379702ffa9a7b6b6964a115037a632ec 100644
--- a/doc/hooks/custom_hooks.md
+++ b/doc/hooks/custom_hooks.md
@@ -2,7 +2,7 @@
 
 **Note: Custom git hooks must be configured on the filesystem of the GitLab
 server. Only GitLab server administrators will be able to complete these tasks.
-Please explore [webhooks](doc/web_hooks/web_hooks.md) as an option if you do not have filesystem access. For a user configurable Git Hooks interface, please see [GitLab Enterprise Edition Git Hooks](http://doc.gitlab.com/ee/git_hooks/git_hooks.html).**
+Please explore [webhooks](../web_hooks/web_hooks.md) as an option if you do not have filesystem access. For a user configurable Git Hooks interface, please see [GitLab Enterprise Edition Git Hooks](http://doc.gitlab.com/ee/git_hooks/git_hooks.html).**
 
 Git natively supports hooks that are executed on different actions.
 Examples of server-side git hooks include pre-receive, post-receive, and update.
diff --git a/doc/install/installation.md b/doc/install/installation.md
index aa989417c4b991eb802eb1decb309a0ba30dc151..bffbc776500a72d7d83d8e167bf062910ea709d0 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -76,7 +76,7 @@ Make sure you have the right version of Git installed
     # Install Git
     sudo apt-get install -y git-core
 
-    # Make sure Git is version 2.7.3 or higher
+    # Make sure Git is version 2.7.4 or higher
     git --version
 
 Is the system packaged Git too old? Remove it and compile from source.
@@ -89,9 +89,9 @@ Is the system packaged Git too old? Remove it and compile from source.
 
     # Download and compile from source
     cd /tmp
-    curl -O --progress https://www.kernel.org/pub/software/scm/git/git-2.7.3.tar.gz
-    echo '30d067499b61caddedaf1a407b4947244f14d10842d100f7c7c6ea1c288280cd  git-2.7.3.tar.gz' | shasum -a256 -c - && tar -xzf git-2.7.3.tar.gz
-    cd git-2.7.3/
+    curl -O --progress https://www.kernel.org/pub/software/scm/git/git-2.7.4.tar.gz
+    echo '7104c4f5d948a75b499a954524cb281fe30c6649d8abe20982936f75ec1f275b  git-2.7.4.tar.gz' | shasum -a256 -c - && tar -xzf git-2.7.4.tar.gz
+    cd git-2.7.4/
     ./configure
     make prefix=/usr/local all
 
@@ -161,7 +161,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
 
     # Install the database packages
     sudo apt-get install -y postgresql postgresql-client libpq-dev
-    
+
     # Create a user for GitLab
     sudo -u postgres psql -d template1 -c "CREATE USER git CREATEDB;"
 
@@ -348,7 +348,7 @@ GitLab Shell is an SSH access and repository management software developed speci
     cd /home/git
     sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
     cd gitlab-workhorse
-    sudo -u git -H git checkout 0.6.5
+    sudo -u git -H git checkout v0.7.1
     sudo -u git -H make
 
 ### Initialize Database and Activate Advanced Features
diff --git a/doc/intro/README.md b/doc/intro/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..fecbbe6317b0a63c4585da46d253db3f67845c1a
--- /dev/null
+++ b/doc/intro/README.md
@@ -0,0 +1,41 @@
+# Get started with GitLab
+
+## Organize
+
+Create projects and groups.
+
+- [Create a new project](../gitlab-basics/create-project.md)
+- [Create a new group](../gitlab-basics/create-group.md)
+
+## Prioritize
+
+Create issues, labels, milestones, cast your vote, and review issues.
+
+- [Create a new issue](../gitlab-basics/create-issue.md)
+- [Assign labels to issues](../workflow/labels.md)
+- [Use milestones as an overview of your project's tracker](../workflow/milestones.md)
+- [Use voting to express your like/dislike to issues and merge requests](../workflow/award_emoji.md)
+
+## Collaborate
+
+Create merge requests and review code.
+
+- [Fork a project and contribute to it](../workflow/forking_workflow.md)
+- [Create a new merge request](../gitlab-basics/add-merge-request.md)
+- [Automatically close issues from merge requests](../customization/issue_closing.md)
+- [Automatically merge when your builds succeed](../workflow/merge_when_build_succeeds.md)
+- [Revert any commit](../workflow/revert_changes.md)
+
+## Test and Deploy
+
+Use the built-in continuous integration in GitLab.
+
+- [Get started with GitLab CI](../ci/quick_start/README.md)
+
+## Install and Update
+
+Install and update your GitLab installation.
+
+- [Install GitLab](https://about.gitlab.com/installation/)
+- [Update GitLab](https://about.gitlab.com/update/)
+- [Explore Omnibus GitLab configuration options](http://doc.gitlab.com/omnibus/settings/configuration.html)
diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md
index 6e22ea7b72af8780ea6fba1fdb5a46213f8c584a..20aa90f0d697e148397fc26be3dfdeca99398249 100644
--- a/doc/public_access/public_access.md
+++ b/doc/public_access/public_access.md
@@ -35,6 +35,21 @@ the repository.
 1. Go to your project's **Settings**
 1. Change "Visibility Level" to either Public, Internal or Private
 
+## Visibility of groups
+
+>**Note:**
+[Starting with][3323] GitLab 8.6, the group visibility has changed and can be
+configured the same way as projects. In previous versions, a group's page was
+always visible to all users.
+
+Like with projects, the visibility of a group can be set to dictate whether
+anonymous users, all signed in users, or only explicit group members can view
+it. The restriction for visibility levels on the application setting level also
+applies to groups, so if that's set to internal, the explore page will be empty
+for anonymous users. The group page now has a visibility level icon.
+
+[3323]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3323
+
 ## Visibility of users
 
 The public page of a user, located at `/u/username`, is always visible whether
@@ -43,14 +58,6 @@ you are logged in or not.
 When visiting the public page of a user, you can only see the projects which
 you are privileged to.
 
-## Visibility of groups
-
-The public page of a group, located at `/groups/groupname`, is always visible
-to everyone.
-
-Logged out users will be able to see the description and the avatar of the
-group as well as all public projects belonging to that group.
-
 ## Restricting the use of public or internal projects
 
 In the Admin area under **Settings** (`/admin/application_settings`), you can
diff --git a/doc/release/security.md b/doc/release/security.md
index b1a62b333e64f1489986d4873c51c81887bcf54f..118c016ba4f2bc54a760383a718b549b8c720139 100644
--- a/doc/release/security.md
+++ b/doc/release/security.md
@@ -15,7 +15,7 @@ Please report suspected security vulnerabilities in private to <support@gitlab.c
 1. Verify that the issue can be reproduced
 1. Acknowledge the issue to the researcher that disclosed it
 1. Inform the release manager that there needs to be a security release
-1. Do the steps from [patch release document](doc/release/patch.md), starting with "Create an issue on private GitLab development server"
+1. Do the steps from [patch release document](../release/patch.md), starting with "Create an issue on private GitLab development server"
 1. The MR with the security fix should get a 'security' label and be assigned to the release manager
 1. Build the package for GitLab.com and do a deploy
 1. Build the package for ci.gitLab.com and do a deploy
diff --git a/doc/security/two_factor_authentication.md b/doc/security/two_factor_authentication.md
index 8365bdb7b1b38b504cf1cf9db1b6ad154868623d..c8499380c18f597d96fd1e8de77153856f1044c7 100644
--- a/doc/security/two_factor_authentication.md
+++ b/doc/security/two_factor_authentication.md
@@ -6,7 +6,7 @@ password to login, they'll be prompted for a code generated by an application on
 their phone.
 
 You can read more about it here:
-[Two-factor Authentication (2FA)](doc/profile/two_factor_authentication.md)
+[Two-factor Authentication (2FA)](../profile/two_factor_authentication.md)
 
 ## Enabling 2FA
 
diff --git a/doc/update/8.2-to-8.3.md b/doc/update/8.2-to-8.3.md
index 2ca4e1f37702308a506594766e9c70221605be6e..9f5c6c4dc84ba652c11bab96406f32601c9a277f 100644
--- a/doc/update/8.2-to-8.3.md
+++ b/doc/update/8.2-to-8.3.md
@@ -1,5 +1,14 @@
 # From 8.2 to 8.3
 
+Make sure you view this update guide from the tag (version) of GitLab you would
+like to install. In most cases this should be the highest numbered production
+tag (without rc in it). You can select the tag in the version dropdown at the
+top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
 **NOTE:** GitLab 8.0 introduced several significant changes related to
 installation and configuration which *are not duplicated here*. Be sure you're
 already running a working version of at least 8.0 before proceeding with this
diff --git a/doc/update/8.3-to-8.4.md b/doc/update/8.3-to-8.4.md
index 269deec7a9c59413e36a8b01a71d0985ae1acfc7..9f6517d9487e636e0b10027bae5dcfaf2545519f 100644
--- a/doc/update/8.3-to-8.4.md
+++ b/doc/update/8.3-to-8.4.md
@@ -1,5 +1,14 @@
 # From 8.3 to 8.4
 
+Make sure you view this update guide from the tag (version) of GitLab you would
+like to install. In most cases this should be the highest numbered production
+tag (without rc in it). You can select the tag in the version dropdown at the
+top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
 ### 1. Stop server
 
     sudo service gitlab stop
diff --git a/doc/update/8.4-to-8.5.md b/doc/update/8.4-to-8.5.md
index 0a9cb5683e7a8e314d51bd6c6012d9cd5f5f42c5..0cb137a03cc337cd609873864d9f375c760b6c28 100644
--- a/doc/update/8.4-to-8.5.md
+++ b/doc/update/8.4-to-8.5.md
@@ -1,5 +1,14 @@
 # From 8.4 to 8.5
 
+Make sure you view this update guide from the tag (version) of GitLab you would
+like to install. In most cases this should be the highest numbered production
+tag (without rc in it). You can select the tag in the version dropdown at the
+top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
 ### 1. Stop server
 
     sudo service gitlab stop
diff --git a/doc/update/8.5-to-8.6.md b/doc/update/8.5-to-8.6.md
index 024f6e8a4333fa0d38a93d2fb041e85ead78e667..712e9fdf93ac70f3c48cf1fa6107c9a7f0bbb84a 100644
--- a/doc/update/8.5-to-8.6.md
+++ b/doc/update/8.5-to-8.6.md
@@ -1,5 +1,14 @@
 # From 8.5 to 8.6
 
+Make sure you view this update guide from the tag (version) of GitLab you would
+like to install. In most cases this should be the highest numbered production
+tag (without rc in it). You can select the tag in the version dropdown at the
+top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
 ### 1. Stop server
 
     sudo service gitlab stop
@@ -49,7 +58,7 @@ GitLab 8.1.
 ```bash
 cd /home/git/gitlab-workhorse
 sudo -u git -H git fetch --all
-sudo -u git -H git checkout 0.6.5
+sudo -u git -H git checkout v0.7.1
 sudo -u git -H make
 ```
 
diff --git a/doc/workflow/award_emoji.md b/doc/workflow/award_emoji.md
new file mode 100644
index 0000000000000000000000000000000000000000..70b35c58be69756c3f3aba873547112fc1a85277
--- /dev/null
+++ b/doc/workflow/award_emoji.md
@@ -0,0 +1,48 @@
+# Award emojis
+
+>**Note:**
+This feature was [introduced][1825] in GitLab 8.2.
+
+When you're collaborating online, you get fewer opportunities for high-fives
+and thumbs-ups. In order to make virtual celebrations easier, you can now vote
+on issues and merge requests using emoji!
+
+![Award emoji](img/award_emoji_select.png)
+
+This makes it much easier to give and receive feedback, without a long comment
+thread. Any comment that contains only the thumbs up or down emojis is
+converted to a vote and depicted in the emoji area.
+
+You can then use that functionality to sort issues and merge requests based on
+popularity.
+
+## Sort issues and merge requests on vote count
+
+>**Note:**
+This feature was [introduced][2871] in GitLab 8.5.
+
+You can quickly sort the issues or merge requests by the number of votes they
+have received. The sort option can be found in the right dropdown menu.
+
+![Votes sort options](img/award_emoji_votes_sort_options.png)
+
+---
+
+Sort by most popular issues/merge requests.
+
+![Votes sort by most popular](img/award_emoji_votes_most_popular.png)
+
+---
+
+Sort by least popular issues/merge requests.
+
+![Votes sort by least popular](img/award_emoji_votes_least_popular.png)
+
+---
+
+The number of upvotes and downvotes is not summed up. That means that an issue
+with 18 upvotes and 5 downvotes is considered more popular than an issue with
+17 upvotes and no downvotes.
+
+[2871]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2781
+[1825]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/1825
diff --git a/doc/workflow/img/award_emoji_select.png b/doc/workflow/img/award_emoji_select.png
new file mode 100644
index 0000000000000000000000000000000000000000..fffdfedda5d7536fbc083cde4efac8e489bedf52
Binary files /dev/null and b/doc/workflow/img/award_emoji_select.png differ
diff --git a/doc/workflow/img/award_emoji_votes_least_popular.png b/doc/workflow/img/award_emoji_votes_least_popular.png
new file mode 100644
index 0000000000000000000000000000000000000000..2ef5be7154f020d0a6e775abae42d4cb7a961b02
Binary files /dev/null and b/doc/workflow/img/award_emoji_votes_least_popular.png differ
diff --git a/doc/workflow/img/award_emoji_votes_most_popular.png b/doc/workflow/img/award_emoji_votes_most_popular.png
new file mode 100644
index 0000000000000000000000000000000000000000..5b089730d936cff3788200889216eb85ff625654
Binary files /dev/null and b/doc/workflow/img/award_emoji_votes_most_popular.png differ
diff --git a/doc/workflow/img/award_emoji_votes_sort_options.png b/doc/workflow/img/award_emoji_votes_sort_options.png
new file mode 100644
index 0000000000000000000000000000000000000000..9bbf3f82a0bdda442f3e908ebaa4d85a2fd78c8a
Binary files /dev/null and b/doc/workflow/img/award_emoji_votes_sort_options.png differ
diff --git a/doc/workflow/importing/import_projects_from_bitbucket.md b/doc/workflow/importing/import_projects_from_bitbucket.md
index 1e9825e2e1049c2be12b6899147eabafd5be8978..520c42162957bf9e1981a53dca79c03ff2d7a9fe 100644
--- a/doc/workflow/importing/import_projects_from_bitbucket.md
+++ b/doc/workflow/importing/import_projects_from_bitbucket.md
@@ -1,6 +1,6 @@
 # Import your project from Bitbucket to GitLab
 
-It takes just a few steps to import your existing Bitbucket projects to GitLab. But keep in mind that it is possible only if Bitbucket support is enabled on your GitLab instance. You can read more about Bitbucket support [here](doc/integration/bitbucket.md).
+It takes just a few steps to import your existing Bitbucket projects to GitLab. But keep in mind that it is possible only if Bitbucket support is enabled on your GitLab instance. You can read more about Bitbucket support [here](../../integration/bitbucket.md).
 
 * Sign in to GitLab.com and go to your dashboard
 
diff --git a/doc/workflow/protected_branches.md b/doc/workflow/protected_branches.md
index fdf9a8d391cb490ec6b24115c3de49bff92056ac..d854ec1e0250643b27ea072c0d530cbb063694da 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](doc/permissions/permissions.md).
+To protect a branch, user needs to have at least a Master permission level, see [permissions document](../permissions/permissions.md).
 
 ![protected branches page](protected_branches/protected_branches1.png)
 
diff --git a/features/project/issues/award_emoji.feature b/features/project/issues/award_emoji.feature
index 2945bb3753a93bfe3439c92445d791f9edaf2fdf..f0fd414a9f913fe50a5b463a4369932d7bed73cb 100644
--- a/features/project/issues/award_emoji.feature
+++ b/features/project/issues/award_emoji.feature
@@ -18,21 +18,24 @@ Feature: Award Emoji
   @javascript
   Scenario: I add and remove custom award in the issue
     Given I click to emoji-picker
-    Then The search field is focused
-    And I click to emoji in the picker
+    Then The emoji menu is visible
+    And The search field is focused
+    Then I click to emoji in the picker
     Then I have award added
     And I can remove it by clicking to icon
 
   @javascript
   Scenario: I can see the list of emoji categories
     Given I click to emoji-picker
-    Then The search field is focused
+    Then The emoji menu is visible
+    And The search field is focused
     Then I can see the activity and food categories
 
   @javascript
   Scenario: I can search emoji
     Given I click to emoji-picker
-    Then The search field is focused
+    Then The emoji menu is visible
+    And The search field is focused
     And I search "hand"
     Then I see search result for "hand"
 
diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature
index ff21c7d1b83d3a73c143aed2b49b99171b242200..de7e2b37725760bbed93269f04555e89c0c2e2ee 100644
--- a/features/project/issues/issues.feature
+++ b/features/project/issues/issues.feature
@@ -160,6 +160,7 @@ Feature: Project Issues
 
   Scenario: Issues on empty project
     Given empty project "Empty Project"
+    And I have an ssh key
     When I visit empty project page
     And I see empty project details with ssh clone info
     When I visit empty project's issues page
diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature
index 74685d24a7d86aabd24adab183907a76782383e0..823658b4f240657b768e5d8f59a48ffed4bba3bf 100644
--- a/features/project/merge_requests.feature
+++ b/features/project/merge_requests.feature
@@ -325,3 +325,11 @@ Feature: Project Merge Requests
     When I click the "Target branch" dropdown
     And I select a new target branch
     Then I should see new target branch changes
+
+  @javascript
+  Scenario: I can close merge request after commenting
+    Given I visit merge request page "Bug NS-04"
+    And I leave a comment like "XML attached"
+    Then I should see comment "XML attached"
+    And I click link "Close"
+    Then I should see closed merge request "Bug NS-04"
diff --git a/features/steps/dashboard/todos.rb b/features/steps/dashboard/todos.rb
index 9722a5a848c546df507c36e7c8f2c2ede3079e67..963e4f21365293d148e7b9c74a94a0455491e852 100644
--- a/features/steps/dashboard/todos.rb
+++ b/features/steps/dashboard/todos.rb
@@ -41,7 +41,6 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps
       click_link 'Done'
     end
 
-    expect(page).to have_content 'Todo was successfully marked as done.'
     expect(page).to have_content 'To do 3'
     expect(page).to have_content 'Done 1'
     should_not_see_todo "John Doe assigned you merge request !#{merge_request.iid}"
diff --git a/features/steps/groups.rb b/features/steps/groups.rb
index 7a6ae15ffa53e18fd2d31b6eecf396fc2e61979d..e5b7db4c5e39a5157f7751dd760c1f49fe0183d4 100644
--- a/features/steps/groups.rb
+++ b/features/steps/groups.rb
@@ -35,7 +35,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
   end
 
   step 'I should see projects activity feed' do
-    expect(page).to have_content 'closed issue'
+    expect(page).to have_content 'joined project'
   end
 
   step 'I should see issues from group "Owned" assigned to me' do
diff --git a/features/steps/project/create.rb b/features/steps/project/create.rb
index 8a0e8fc2b6c8998937c4140558cc882e877d5a20..422b151eaa241829b9bffbe199a001c55f1775a5 100644
--- a/features/steps/project/create.rb
+++ b/features/steps/project/create.rb
@@ -27,7 +27,7 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps
 
   step 'I click on HTTP' do
     find('#clone-dropdown').click
-    find('#http-selector').click
+    find('.http-selector').click
   end
 
   step 'Remote url should update to http link' do
@@ -36,7 +36,7 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps
 
   step 'If I click on SSH' do
     find('#clone-dropdown').click
-    find('#ssh-selector').click
+    find('.ssh-selector').click
   end
 
   step 'Remote url should update to ssh link' do
diff --git a/features/steps/project/issues/award_emoji.rb b/features/steps/project/issues/award_emoji.rb
index ce2554bc80dd89248d7d118f97d87ac4cbc518fb..c5d45709b445675d6e6dde3ce0662c9ced86a87b 100644
--- a/features/steps/project/issues/award_emoji.rb
+++ b/features/steps/project/issues/award_emoji.rb
@@ -92,6 +92,10 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps
     end
   end
 
+  step 'The emoji menu is visible' do
+    page.find(".emoji-menu.is-visible")
+  end
+
   step 'The search field is focused' do
     expect(page).to have_selector('#emoji_search')
     expect(page.evaluate_script('document.activeElement.id')).to eq('emoji_search')
diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb
index 8c31fa890b262fd3939c1a15d1de13bb41f41d4c..aff5ca676beb4e8242010d2bf35d8e730d4f146f 100644
--- a/features/steps/project/issues/issues.rb
+++ b/features/steps/project/issues/issues.rb
@@ -5,6 +5,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
   include SharedNote
   include SharedPaths
   include SharedMarkdown
+  include SharedUser
 
   step 'I should see "Release 0.4" in issues' do
     expect(page).to have_content "Release 0.4"
@@ -240,7 +241,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
   end
 
   step 'empty project "Empty Project"' do
-    create :empty_project, name: 'Empty Project', namespace: @user.namespace
+    create :project_empty_repo, name: 'Empty Project', namespace: @user.namespace
   end
 
   When 'I visit empty project page' do
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 71197205f3433e7f3587cf986c8b82fa1fb029b0..197e826e5bc31a09919860b7cecd8a8f438fa2a3 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -85,7 +85,7 @@ module API
     end
 
     class Group < Grape::Entity
-      expose :id, :name, :path, :description
+      expose :id, :name, :path, :description, :visibility_level
       expose :avatar_url
 
       expose :web_url do |group, options|
@@ -340,6 +340,7 @@ module API
       expose :session_expire_delay
       expose :default_project_visibility
       expose :default_snippet_visibility
+      expose :default_group_visibility
       expose :restricted_signup_domains
       expose :user_oauth_applications
       expose :after_sign_out_path
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 1a14d870a4a396e51ea76c0792112913f1248498..c165de21a75728c5f42ee30206c9ea6a268f44b9 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -31,7 +31,7 @@ module API
         authorize! :create_group, current_user
         required_attributes! [:name, :path]
 
-        attrs = attributes_for_keys [:name, :path, :description]
+        attrs = attributes_for_keys [:name, :path, :description, :visibility_level]
         @group = Group.new(attrs)
 
         if @group.save
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index a72044e8058d2c7cee5373a50640a626661e80a9..4921ae99e788436ab5e824d9bb1a6dd70b94f4ff 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -118,9 +118,7 @@ module API
     end
 
     def authorize!(action, subject)
-      unless abilities.allowed?(current_user, action, subject)
-        forbidden!
-      end
+      forbidden! unless abilities.allowed?(current_user, action, subject)
     end
 
     def authorize_push_project
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 252744515da88088f8a8b3aa8eb49161db9ab1e4..e5ae88eb96fb9d34b7036c7135c9e0d2495aac20 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -82,7 +82,7 @@ module API
       #   GET /projects/:id/issues?milestone=1.0.0&state=closed
       #   GET /issues?iid=42
       get ":id/issues" do
-        issues = user_project.issues
+        issues = user_project.issues.visible_to_user(current_user)
         issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
         issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
         issues = filter_by_iid(issues, params[:iid]) unless params[:iid].nil?
@@ -104,6 +104,7 @@ module API
       #   GET /projects/:id/issues/:issue_id
       get ":id/issues/:issue_id" do
         @issue = user_project.issues.find(params[:issue_id])
+        not_found! unless can?(current_user, :read_issue, @issue)
         present @issue, with: Entities::Issue
       end
 
@@ -190,7 +191,7 @@ module API
         end
       end
 
-      # Delete a project issue (deprecated)
+      # Delete a project issue
       #
       # Parameters:
       #   id (required) - The ID of a project
@@ -198,7 +199,10 @@ module API
       # Example Request:
       #   DELETE /projects/:id/issues/:issue_id
       delete ":id/issues/:issue_id" do
-        not_allowed!
+        issue = user_project.issues.find_by(id: params[:issue_id])
+
+        authorize!(:destroy_issue, issue)
+        issue.destroy
       end
     end
   end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index c5e5d57ed4d5277ab0ccd18865b9d22145c01f5d..93052fba06be2213fcaa61898ff354ada37b0c50 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -100,6 +100,18 @@ module API
         end
       end
 
+      # Delete a MR
+      #
+      # Parameters:
+      # id (required)               - The ID of the project
+      # merge_request_id (required) - The MR id
+      delete ":id/merge_requests/:merge_request_id" do
+        merge_request = user_project.merge_requests.find_by(id: params[:merge_request_id])
+
+        authorize!(:destroy_merge_request, merge_request)
+        merge_request.destroy
+      end
+
       # Routing "merge_request/:merge_request_id/..." is DEPRECATED and WILL BE REMOVED in version 9.0
       # Use "merge_requests/:merge_request_id/..." instead.
       #
diff --git a/lib/banzai/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb
index 9f08aa36e8b5bfd9cc541ee08bc65452d2f27212..2732e0b51455cadfebabf4b936fbf319e854f19c 100644
--- a/lib/banzai/filter/issue_reference_filter.rb
+++ b/lib/banzai/filter/issue_reference_filter.rb
@@ -9,6 +9,11 @@ module Banzai
         Issue
       end
 
+      def self.user_can_see_reference?(user, node, context)
+        issue = Issue.find(node.attr('data-issue')) rescue nil
+        Ability.abilities.allowed?(user, :read_issue, issue)
+      end
+
       def find_object(project, id)
         project.get_issue(id)
       end
diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb
index 3637b1bac94850b6dbd2b28752df881d4a671f18..132f0a4bd93fab26b9529b68eefcf23e5ee937fa 100644
--- a/lib/banzai/filter/reference_filter.rb
+++ b/lib/banzai/filter/reference_filter.rb
@@ -47,6 +47,7 @@ module Banzai
       # Returns a String
       def data_attribute(attributes = {})
         attributes[:reference_filter] = self.class.name.demodulize
+        attributes.delete(:original) if context[:no_original_data]
         attributes.map { |key, value| %Q(data-#{key.to_s.dasherize}="#{escape_once(value)}") }.join(" ")
       end
 
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index c89e1b5101933cbd11cabb3f186c4226221fcdd0..2228425076bcf615baae833091c0e4ec2d2f677d 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -26,8 +26,8 @@ module Ci
       validate!
     end
 
-    def builds_for_stage_and_ref(stage, ref, tag = false)
-      builds.select{|build| build[:stage] == stage && process?(build[:only], build[:except], ref, tag)}
+    def builds_for_stage_and_ref(stage, ref, tag = false, trigger_request = nil)
+      builds.select{|build| build[:stage] == stage && process?(build[:only], build[:except], ref, tag, trigger_request)}
     end
 
     def builds
@@ -266,29 +266,30 @@ module Ci
       value.in?([true, false])
     end
 
-    def process?(only_params, except_params, ref, tag)
+    def process?(only_params, except_params, ref, tag, trigger_request)
       if only_params.present?
-        return false unless matching?(only_params, ref, tag)
+        return false unless matching?(only_params, ref, tag, trigger_request)
       end
 
       if except_params.present?
-        return false if matching?(except_params, ref, tag)
+        return false if matching?(except_params, ref, tag, trigger_request)
       end
 
       true
     end
 
-    def matching?(patterns, ref, tag)
+    def matching?(patterns, ref, tag, trigger_request)
       patterns.any? do |pattern|
-        match_ref?(pattern, ref, tag)
+        match_ref?(pattern, ref, tag, trigger_request)
       end
     end
 
-    def match_ref?(pattern, ref, tag)
+    def match_ref?(pattern, ref, tag, trigger_request)
       pattern, path = pattern.split('@', 2)
       return false if path && path != self.path
       return true if tag && pattern == 'tags'
       return true if !tag && pattern == 'branches'
+      return true if trigger_request.present? && pattern == 'triggers'
 
       if pattern.first == "/" && pattern.last == "/"
         Regexp.new(pattern[1...-1]) =~ ref
diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb
index faa2830c16e434ab02beb48685ba4a790a56061a..d2e85cabf72c18b5d4885925a6321fa7b8b58287 100644
--- a/lib/gitlab/diff/file.rb
+++ b/lib/gitlab/diff/file.rb
@@ -24,6 +24,10 @@ module Gitlab
         @lines ||= parser.parse(raw_diff.each_line).to_a
       end
 
+      def too_large?
+        diff.too_large?
+      end
+
       def highlighted_diff_lines
         Gitlab::Diff::Highlight.new(self).highlight
       end
diff --git a/lib/gitlab/gfm/reference_rewriter.rb b/lib/gitlab/gfm/reference_rewriter.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a1c6ee7bd6908642d6b2c9fc401f6f7c1e8cf34e
--- /dev/null
+++ b/lib/gitlab/gfm/reference_rewriter.rb
@@ -0,0 +1,79 @@
+module Gitlab
+  module Gfm
+    ##
+    # Class that unfolds local references in text.
+    #
+    # The initializer takes text in Markdown and project this text is valid
+    # in context of.
+    #
+    # `unfold` method tries to find all local references and unfold each of
+    # those local references to cross reference format, assuming that the
+    # argument passed to this method is a project that references will be
+    # viewed from (see `Referable#to_reference method).
+    #
+    # Examples:
+    #
+    # 'Hello, this issue is related to #123 and
+    #  other issues labeled with ~"label"', will be converted to:
+    #
+    # 'Hello, this issue is related to gitlab-org/gitlab-ce#123 and
+    #  other issue labeled with gitlab-org/gitlab-ce~"label"'.
+    #
+    # It does respect markdown lexical rules, so text in code block will not be
+    # replaced, see another example:
+    #
+    # 'Merge request for issue #1234, see also link:
+    #  http://gitlab.com/some/link/#1234, and code `puts #1234`' =>
+    #
+    # 'Merge request for issue gitlab-org/gitlab-ce#1234, se also link:
+    #  http://gitlab.com/some/link/#1234, and code `puts #1234`'
+    #
+    class ReferenceRewriter
+      def initialize(text, source_project, current_user)
+        @text = text
+        @source_project = source_project
+        @current_user = current_user
+        @original_html = markdown(text)
+      end
+
+      def rewrite(target_project)
+        pattern = Gitlab::ReferenceExtractor.references_pattern
+
+        @text.gsub(pattern) do |reference|
+          unfold_reference(reference, Regexp.last_match, target_project)
+        end
+      end
+
+      private
+
+      def unfold_reference(reference, match, target_project)
+        before = @text[0...match.begin(0)]
+        after = @text[match.end(0)..-1]
+
+        referable = find_referable(reference)
+        return reference unless referable
+
+        cross_reference = referable.to_reference(target_project)
+        return reference if reference == cross_reference
+
+        new_text = before + cross_reference + after
+        substitution_valid?(new_text) ? cross_reference : reference
+      end
+
+      def find_referable(reference)
+        extractor = Gitlab::ReferenceExtractor.new(@source_project,
+                                                   @current_user)
+        extractor.analyze(reference)
+        extractor.all.first
+      end
+
+      def substitution_valid?(substituted)
+        @original_html == markdown(substituted)
+      end
+
+      def markdown(text)
+        Banzai.render(text, project: @source_project, no_original_data: true)
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb
index 0607a8b95927940941509ff216ad98dd3fd839b0..71c5b6801fb66ef94cba8c8533d530be688a17fb 100644
--- a/lib/gitlab/project_search_results.rb
+++ b/lib/gitlab/project_search_results.rb
@@ -2,7 +2,8 @@ module Gitlab
   class ProjectSearchResults < SearchResults
     attr_reader :project, :repository_ref
 
-    def initialize(project, query, repository_ref = nil)
+    def initialize(current_user, project, query, repository_ref = nil)
+      @current_user = current_user
       @project = project
       @repository_ref = if repository_ref.present?
                           repository_ref
diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index 4d830aa45e1cfcc6bdf589a38ea5a5863bd6f708..13c4d64c99b0d9ee59ed0ef73c2d87caa6b8d199 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -1,6 +1,7 @@
 module Gitlab
   # Extract possible GFM references from an arbitrary String for further processing.
   class ReferenceExtractor < Banzai::ReferenceExtractor
+    REFERABLES = %i(user issue label milestone merge_request snippet commit commit_range)
     attr_accessor :project, :current_user, :author
 
     def initialize(project, current_user = nil, author = nil)
@@ -17,7 +18,7 @@ module Gitlab
       super(text, context.merge(project: project))
     end
 
-    %i(user label milestone merge_request snippet commit commit_range).each do |type|
+    REFERABLES.each do |type|
       define_method("#{type}s") do
         @references[type] ||= references(type, reference_context)
       end
@@ -31,6 +32,21 @@ module Gitlab
       end
     end
 
+    def all
+      REFERABLES.each { |referable| send(referable.to_s.pluralize) }
+      @references.values.flatten
+    end
+
+    def self.references_pattern
+      return @pattern if @pattern
+
+      patterns = REFERABLES.map do |ref|
+        ref.to_s.classify.constantize.try(:reference_pattern)
+      end
+
+      @pattern = Regexp.union(patterns.compact)
+    end
+
     private
 
     def reference_context
diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb
index f13528a2eea549b2a929ea501edc1d6cc7da6747..f8ab2b1f09ec0acafaee12bcb8d7eb3375983c7d 100644
--- a/lib/gitlab/search_results.rb
+++ b/lib/gitlab/search_results.rb
@@ -1,12 +1,13 @@
 module Gitlab
   class SearchResults
-    attr_reader :query
+    attr_reader :current_user, :query
 
     # Limit search results by passed projects
     # It allows us to search only for projects user has access to
     attr_reader :limit_projects
 
-    def initialize(limit_projects, query)
+    def initialize(current_user, limit_projects, query)
+      @current_user = current_user
       @limit_projects = limit_projects || Project.all
       @query = Shellwords.shellescape(query) if query.present?
     end
@@ -58,7 +59,7 @@ module Gitlab
     end
 
     def issues
-      issues = Issue.where(project_id: project_ids_relation)
+      issues = Issue.visible_to_user(current_user).where(project_id: project_ids_relation)
 
       if query =~ /#(\d+)\z/
         issues = issues.where(iid: $1)
diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb
index 3160a3c7582547aa117bae1285e0e3ce9424e2d4..a1ee1cba216bcdb8cb55f25b376c7b795b102620 100644
--- a/lib/gitlab/visibility_level.rb
+++ b/lib/gitlab/visibility_level.rb
@@ -6,6 +6,14 @@
 module Gitlab
   module VisibilityLevel
     extend CurrentSettings
+    extend ActiveSupport::Concern
+
+    included do
+      scope :public_only,               -> { where(visibility_level: PUBLIC) }
+      scope :public_and_internal_only,  -> { where(visibility_level: [PUBLIC, INTERNAL] ) }
+
+      scope :public_to_user, -> (user) { user && !user.external ? public_and_internal_only : public_only }
+    end
 
     PRIVATE  = 0 unless const_defined?(:PRIVATE)
     INTERNAL = 10 unless const_defined?(:INTERNAL)
@@ -48,10 +56,6 @@ module Gitlab
         options.has_value?(level)
       end
 
-      def allowed_fork_levels(origin_level)
-        [PRIVATE, INTERNAL, PUBLIC].select{ |level| level <= origin_level }
-      end
-
       def level_name(level)
         level_name = 'Unknown'
         options.each do |name, lvl|
diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab
index fc5475c4eef881b2b67e8bcc231f622144b93fa0..1324e4cd267dab602e6ff13487d6b2d397ff75dd 100644
--- a/lib/support/nginx/gitlab
+++ b/lib/support/nginx/gitlab
@@ -30,7 +30,6 @@ server {
   listen [::]:80 default_server;
   server_name YOUR_SERVER_FQDN; ## Replace this with something like gitlab.example.com
   server_tokens off; ## Don't show the nginx version number, a security best practice
-  root /home/git/gitlab/public;
 
   ## See app/controllers/application_controller.rb for headers set
 
@@ -57,4 +56,14 @@ server {
 
     proxy_pass http://gitlab-workhorse;
   }
+
+  error_page 404 /404.html;
+  error_page 422 /422.html;
+  error_page 500 /500.html;
+  error_page 502 /502.html;
+  location ~ ^/(404|422|500|502)\.html$ {
+    root /home/git/gitlab/public;
+    internal;
+  }
+
 }
diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl
index 1e5f85413ec6dd8d124c4795f3b9dbfa32acfa86..af6ea9ed7064d3f0d043370a8b4ea4cd8d503979 100644
--- a/lib/support/nginx/gitlab-ssl
+++ b/lib/support/nginx/gitlab-ssl
@@ -45,7 +45,6 @@ server {
   listen [::]:443 ipv6only=on ssl default_server;
   server_name YOUR_SERVER_FQDN; ## Replace this with something like gitlab.example.com
   server_tokens off; ## Don't show the nginx version number, a security best practice
-  root /home/git/gitlab/public;
 
   ## Strong SSL Security
   ## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/
@@ -101,4 +100,13 @@ server {
     proxy_set_header    X-Forwarded-Proto   $scheme;
     proxy_pass http://gitlab-workhorse;
   }
+
+  error_page 404 /404.html;
+  error_page 422 /422.html;
+  error_page 500 /500.html;
+  error_page 502 /502.html;
+  location ~ ^/(404|422|500|502)\.html$ {
+    root /home/git/gitlab/public;
+    internal;
+  }
 }
diff --git a/public/404.html b/public/404.html
index a0106bc760dc719c0f577ecbea4832fdb92fb16d..4862770cc2a7d55621fded6960c4c32e92040f0a 100644
--- a/public/404.html
+++ b/public/404.html
@@ -2,11 +2,51 @@
 <html>
 <head>
   <title>The page you're looking for could not be found (404)</title>
-  <link href="/static.css" media="screen" rel="stylesheet" type="text/css" />
+  <style>
+      body {
+        color: #666;
+        text-align: center;
+        font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+        margin: 0;
+        width: 800px;
+        margin: auto;
+        font-size: 14px;
+      }
+
+      h1 {
+        font-size: 56px;
+        line-height: 100px;
+        font-weight: normal;
+        color: #456;
+      }
+
+      h2 {
+        font-size: 24px;
+        color: #666;
+        line-height: 1.5em;
+      }
+
+      h3 {
+        color: #456;
+        font-size: 20px;
+        font-weight: normal;
+        line-height: 28px;
+      }
+
+      hr {
+        margin: 18px 0;
+        border: 0;
+        border-top: 1px solid #EEE;
+        border-bottom: 1px solid white;
+      }
+  </style>
 </head>
 
 <body>
-  <h1>404</h1>
+  <h1>
+    <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEwIiBoZWlnaHQ9IjIxMCIgdmlld0JveD0iMCAwIDIxMCAyMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTVsMzguNjQtMTE4LjkyMWgtNzcuMjhsMzguNjQgMTE4LjkyMXoiIGZpbGw9IiNlMjQzMjkiLz4KICA8cGF0aCBkPSJNMTA1LjA2MTQgMjAzLjY1NDhsLTM4LjY0LTExOC45MjFoLTU0LjE1M2w5Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTIuMjY4NSA4NC43MzQxbC0xMS43NDIgMzYuMTM5Yy0xLjA3MSAzLjI5Ni4xMDIgNi45MDcgMi45MDYgOC45NDRsMTAxLjYyOSA3My44MzgtOTIuNzkzLTExOC45MjF6IiBmaWxsPSIjZmNhMzI2Ii8+CiAgPHBhdGggZD0iTTEyLjI2ODUgODQuNzM0Mmg1NC4xNTNsLTIzLjI3My03MS42MjVjLTEuMTk3LTMuNjg2LTYuNDExLTMuNjg1LTcuNjA4IDBsLTIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTQ4bDM4LjY0LTExOC45MjFoNTQuMTUzbC05Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTk3Ljg1NDQgODQuNzM0MWwxMS43NDIgMzYuMTM5YzEuMDcxIDMuMjk2LS4xMDIgNi45MDctMi45MDYgOC45NDRsLTEwMS42MjkgNzMuODM4IDkyLjc5My0xMTguOTIxeiIgZmlsbD0iI2ZjYTMyNiIvPgogIDxwYXRoIGQ9Ik0xOTcuODU0NCA4NC43MzQyaC01NC4xNTNsMjMuMjczLTcxLjYyNWMxLjE5Ny0zLjY4NiA2LjQxMS0zLjY4NSA3LjYwOCAwbDIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+Cjwvc3ZnPgo=" /><br />
+    404
+  </h1>
   <h3>The page you're looking for could not be found.</h3>
   <hr/>
   <p>Make sure the address is correct and that the page hasn't moved.</p>
diff --git a/public/422.html b/public/422.html
index 026997b48e318aefc0a75619b4fbbfa39f2fc0a9..055b0bde165159e82a4aefc50872b710ae6025bb 100644
--- a/public/422.html
+++ b/public/422.html
@@ -2,12 +2,51 @@
 <html>
 <head>
   <title>The change you requested was rejected (422)</title>
-  <link href="/static.css" media="screen" rel="stylesheet" type="text/css" />
+  <style>
+    body {
+      color: #666;
+       text-align: center;
+       font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+       margin: 0;
+       width: 800px;
+       margin: auto;
+       font-size: 14px;
+     }
+
+     h1 {
+       font-size: 56px;
+       line-height: 100px;
+       font-weight: normal;
+       color: #456;
+     }
+
+     h2 {
+       font-size: 24px;
+       color: #666;
+       line-height: 1.5em;
+     }
+
+     h3 {
+       color: #456;
+       font-size: 20px;
+       font-weight: normal;
+       line-height: 28px;
+     }
+
+     hr {
+       margin: 18px 0;
+       border: 0;
+       border-top: 1px solid #EEE;
+       border-bottom: 1px solid white;
+     }
+   </style>
 </head>
 
 <body>
-  <!-- This file lives in public/422.html -->
-  <h1>422</h1>
+  <h1>
+    <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEwIiBoZWlnaHQ9IjIxMCIgdmlld0JveD0iMCAwIDIxMCAyMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTVsMzguNjQtMTE4LjkyMWgtNzcuMjhsMzguNjQgMTE4LjkyMXoiIGZpbGw9IiNlMjQzMjkiLz4KICA8cGF0aCBkPSJNMTA1LjA2MTQgMjAzLjY1NDhsLTM4LjY0LTExOC45MjFoLTU0LjE1M2w5Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTIuMjY4NSA4NC43MzQxbC0xMS43NDIgMzYuMTM5Yy0xLjA3MSAzLjI5Ni4xMDIgNi45MDcgMi45MDYgOC45NDRsMTAxLjYyOSA3My44MzgtOTIuNzkzLTExOC45MjF6IiBmaWxsPSIjZmNhMzI2Ii8+CiAgPHBhdGggZD0iTTEyLjI2ODUgODQuNzM0Mmg1NC4xNTNsLTIzLjI3My03MS42MjVjLTEuMTk3LTMuNjg2LTYuNDExLTMuNjg1LTcuNjA4IDBsLTIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTQ4bDM4LjY0LTExOC45MjFoNTQuMTUzbC05Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTk3Ljg1NDQgODQuNzM0MWwxMS43NDIgMzYuMTM5YzEuMDcxIDMuMjk2LS4xMDIgNi45MDctMi45MDYgOC45NDRsLTEwMS42MjkgNzMuODM4IDkyLjc5My0xMTguOTIxeiIgZmlsbD0iI2ZjYTMyNiIvPgogIDxwYXRoIGQ9Ik0xOTcuODU0NCA4NC43MzQyaC01NC4xNTNsMjMuMjczLTcxLjYyNWMxLjE5Ny0zLjY4NiA2LjQxMS0zLjY4NSA3LjYwOCAwbDIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+Cjwvc3ZnPgo=" /><br />
+    422
+  </h1>
   <h3>The change you requested was rejected.</h3>
   <hr />
   <p>Make sure you have access to the thing you tried to change.</p>
diff --git a/public/500.html b/public/500.html
index 08c11bbd05a330b5bdcc21b38e1fab4e78e64d88..3d59d1392f5ee7b0be736c595971a9ddb7d3cc1a 100644
--- a/public/500.html
+++ b/public/500.html
@@ -2,10 +2,50 @@
 <html>
 <head>
   <title>Something went wrong (500)</title>
-  <link href="/static.css" media="screen" rel="stylesheet" type="text/css" />
+  <style>
+     body {
+       color: #666;
+       text-align: center;
+       font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+       margin: 0;
+       width: 800px;
+       margin: auto;
+       font-size: 14px;
+     }
+
+     h1 {
+       font-size: 56px;
+       line-height: 100px;
+       font-weight: normal;
+       color: #456;
+     }
+
+     h2 {
+       font-size: 24px;
+       color: #666;
+       line-height: 1.5em;
+     }
+
+     h3 {
+       color: #456;
+       font-size: 20px;
+       font-weight: normal;
+       line-height: 28px;
+     }
+
+     hr {
+      margin: 18px 0;
+      border: 0;
+      border-top: 1px solid #EEE;
+      border-bottom: 1px solid white;
+    }
+  </style>
 </head>
 <body>
-  <h1>500</h1>
+  <h1>
+    <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEwIiBoZWlnaHQ9IjIxMCIgdmlld0JveD0iMCAwIDIxMCAyMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTVsMzguNjQtMTE4LjkyMWgtNzcuMjhsMzguNjQgMTE4LjkyMXoiIGZpbGw9IiNlMjQzMjkiLz4KICA8cGF0aCBkPSJNMTA1LjA2MTQgMjAzLjY1NDhsLTM4LjY0LTExOC45MjFoLTU0LjE1M2w5Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTIuMjY4NSA4NC43MzQxbC0xMS43NDIgMzYuMTM5Yy0xLjA3MSAzLjI5Ni4xMDIgNi45MDcgMi45MDYgOC45NDRsMTAxLjYyOSA3My44MzgtOTIuNzkzLTExOC45MjF6IiBmaWxsPSIjZmNhMzI2Ii8+CiAgPHBhdGggZD0iTTEyLjI2ODUgODQuNzM0Mmg1NC4xNTNsLTIzLjI3My03MS42MjVjLTEuMTk3LTMuNjg2LTYuNDExLTMuNjg1LTcuNjA4IDBsLTIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTQ4bDM4LjY0LTExOC45MjFoNTQuMTUzbC05Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTk3Ljg1NDQgODQuNzM0MWwxMS43NDIgMzYuMTM5YzEuMDcxIDMuMjk2LS4xMDIgNi45MDctMi45MDYgOC45NDRsLTEwMS42MjkgNzMuODM4IDkyLjc5My0xMTguOTIxeiIgZmlsbD0iI2ZjYTMyNiIvPgogIDxwYXRoIGQ9Ik0xOTcuODU0NCA4NC43MzQyaC01NC4xNTNsMjMuMjczLTcxLjYyNWMxLjE5Ny0zLjY4NiA2LjQxMS0zLjY4NSA3LjYwOCAwbDIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+Cjwvc3ZnPgo=" /><br />
+    500
+  </h1>
   <h3>Whoops, something went wrong on our end.</h3>
   <hr/>
   <p>Try refreshing the page, or going back and attempting the action again.</p>
diff --git a/public/502.html b/public/502.html
index 9480a928439e29f269ef76f962fd63f2d4d4a393..67dfd8a27438c6846b7b932f4bbdf50dc7ab3d47 100644
--- a/public/502.html
+++ b/public/502.html
@@ -2,10 +2,50 @@
 <html>
 <head>
   <title>GitLab is not responding (502)</title>
-  <link href="/static.css" media="screen" rel="stylesheet" type="text/css" />
+  <style>
+    body {
+      color: #666;
+      text-align: center;
+      font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+      margin: 0;
+      width: 800px;
+      margin: auto;
+      font-size: 14px;
+    }
+
+    h1 {
+      font-size: 56px;
+      line-height: 100px;
+      font-weight: normal;
+      color: #456;
+    }
+
+    h2 {
+      font-size: 24px;
+      color: #666;
+      line-height: 1.5em;
+    }
+
+    h3 {
+      color: #456;
+      font-size: 20px;
+      font-weight: normal;
+      line-height: 28px;
+    }
+
+    hr {
+      margin: 18px 0;
+      border: 0;
+      border-top: 1px solid #EEE;
+      border-bottom: 1px solid white;
+    }
+  </style>
 </head>
 <body>
-  <h1>502</h1>
+  <h1>
+    <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEwIiBoZWlnaHQ9IjIxMCIgdmlld0JveD0iMCAwIDIxMCAyMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTVsMzguNjQtMTE4LjkyMWgtNzcuMjhsMzguNjQgMTE4LjkyMXoiIGZpbGw9IiNlMjQzMjkiLz4KICA8cGF0aCBkPSJNMTA1LjA2MTQgMjAzLjY1NDhsLTM4LjY0LTExOC45MjFoLTU0LjE1M2w5Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTIuMjY4NSA4NC43MzQxbC0xMS43NDIgMzYuMTM5Yy0xLjA3MSAzLjI5Ni4xMDIgNi45MDcgMi45MDYgOC45NDRsMTAxLjYyOSA3My44MzgtOTIuNzkzLTExOC45MjF6IiBmaWxsPSIjZmNhMzI2Ii8+CiAgPHBhdGggZD0iTTEyLjI2ODUgODQuNzM0Mmg1NC4xNTNsLTIzLjI3My03MS42MjVjLTEuMTk3LTMuNjg2LTYuNDExLTMuNjg1LTcuNjA4IDBsLTIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTQ4bDM4LjY0LTExOC45MjFoNTQuMTUzbC05Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTk3Ljg1NDQgODQuNzM0MWwxMS43NDIgMzYuMTM5YzEuMDcxIDMuMjk2LS4xMDIgNi45MDctMi45MDYgOC45NDRsLTEwMS42MjkgNzMuODM4IDkyLjc5My0xMTguOTIxeiIgZmlsbD0iI2ZjYTMyNiIvPgogIDxwYXRoIGQ9Ik0xOTcuODU0NCA4NC43MzQyaC01NC4xNTNsMjMuMjczLTcxLjYyNWMxLjE5Ny0zLjY4NiA2LjQxMS0zLjY4NSA3LjYwOCAwbDIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+Cjwvc3ZnPgo=" /><br />
+    502
+  </h1>
   <h3>Whoops, GitLab is taking too much time to respond.</h3>
   <hr/>
   <p>Try refreshing the page, or going back and attempting the action again.</p>
diff --git a/public/deploy.html b/public/deploy.html
index 3822ed4b64d673a685faae079a0f2f0f58c6594e..48976dacf41f89a8f1f90c99fb5220c68b70b5bb 100644
--- a/public/deploy.html
+++ b/public/deploy.html
@@ -2,12 +2,49 @@
 <html>
   <head>
     <title>Deploy in progress</title>
-    <link href="/static.css" media="screen" rel="stylesheet" type="text/css" />
+    <style>
+     body {
+        color: #666;
+        text-align: center;
+        font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+        margin: 0;
+        width: 800px;
+        margin: auto;
+        font-size: 14px;
+      }
+
+      h1 {
+        font-size: 56px;
+        line-height: 100px;
+        font-weight: normal;
+        color: #456;
+      }
+
+      h2 {
+        font-size: 24px;
+        color: #666;
+        line-height: 1.5em;
+      }
+
+      h3 {
+        color: #456;
+        font-size: 20px;
+        font-weight: normal;
+        line-height: 28px;
+      }
+
+      hr {
+        margin: 18px 0;
+        border: 0;
+        border-top: 1px solid #EEE;
+        border-bottom: 1px solid white;
+      }
+    </style>
   </head>
 
   <body>
     <h1>
-      <img src="/logo.svg" /><br />
+      <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEwIiBoZWlnaHQ9IjIxMCIgdmlld0JveD0iMCAwIDIxMCAyMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTVsMzguNjQtMTE4LjkyMWgtNzcuMjhsMzguNjQgMTE4LjkyMXoiIGZpbGw9IiNlMjQzMjkiLz4KICA8cGF0aCBkPSJNMTA1LjA2MTQgMjAzLjY1NDhsLTM4LjY0LTExOC45MjFoLTU0LjE1M2w5Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTIuMjY4NSA4NC43MzQxbC0xMS43NDIgMzYuMTM5Yy0xLjA3MSAzLjI5Ni4xMDIgNi45MDcgMi45MDYgOC45NDRsMTAxLjYyOSA3My44MzgtOTIuNzkzLTExOC45MjF6IiBmaWxsPSIjZmNhMzI2Ii8+CiAgPHBhdGggZD0iTTEyLjI2ODUgODQuNzM0Mmg1NC4xNTNsLTIzLjI3My03MS42MjVjLTEuMTk3LTMuNjg2LTYuNDExLTMuNjg1LTcuNjA4IDBsLTIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTQ4bDM4LjY0LTExOC45MjFoNTQuMTUzbC05Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTk3Ljg1NDQgODQuNzM0MWwxMS43NDIgMzYuMTM5YzEuMDcxIDMuMjk2LS4xMDIgNi45MDctMi45MDYgOC45NDRsLTEwMS42MjkgNzMuODM4IDkyLjc5My0xMTguOTIxeiIgZmlsbD0iI2ZjYTMyNiIvPgogIDxwYXRoIGQ9Ik0xOTcuODU0NCA4NC43MzQyaC01NC4xNTNsMjMuMjczLTcxLjYyNWMxLjE5Ny0zLjY4NiA2LjQxMS0zLjY4NSA3LjYwOCAwbDIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+Cjwvc3ZnPgo=" /><br />
       Deploy in progress
     </h1>
     <h3>Please try again in a few minutes.</h3>
diff --git a/public/logo.svg b/public/logo.svg
deleted file mode 100644
index fc4553137f73dfbf2f13a0c0f4ea878742032243..0000000000000000000000000000000000000000
--- a/public/logo.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-<svg width="210" height="210" viewBox="0 0 210 210" xmlns="http://www.w3.org/2000/svg">
-  <path d="M105.0614 203.655l38.64-118.921h-77.28l38.64 118.921z" fill="#e24329"/>
-  <path d="M105.0614 203.6548l-38.64-118.921h-54.153l92.793 118.921z" fill="#fc6d26"/>
-  <path d="M12.2685 84.7341l-11.742 36.139c-1.071 3.296.102 6.907 2.906 8.944l101.629 73.838-92.793-118.921z" fill="#fca326"/>
-  <path d="M12.2685 84.7342h54.153l-23.273-71.625c-1.197-3.686-6.411-3.685-7.608 0l-23.272 71.625z" fill="#e24329"/>
-  <path d="M105.0614 203.6548l38.64-118.921h54.153l-92.793 118.921z" fill="#fc6d26"/>
-  <path d="M197.8544 84.7341l11.742 36.139c1.071 3.296-.102 6.907-2.906 8.944l-101.629 73.838 92.793-118.921z" fill="#fca326"/>
-  <path d="M197.8544 84.7342h-54.153l23.273-71.625c1.197-3.686 6.411-3.685 7.608 0l23.272 71.625z" fill="#e24329"/>
-</svg>
diff --git a/public/static.css b/public/static.css
deleted file mode 100644
index 0a2b6060d486005f104e486c1aeb81274917f2c7..0000000000000000000000000000000000000000
--- a/public/static.css
+++ /dev/null
@@ -1,36 +0,0 @@
-body {
-  color: #666;
-  text-align: center;
-  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
-  margin: 0;
-  width: 800px;
-  margin: auto;
-  font-size: 14px;
-}
-
-h1 {
-  font-size: 56px;
-  line-height: 100px;
-  font-weight: normal;
-  color: #456;
-}
-
-h2 { 
-  font-size: 24px; 
-  color: #666; 
-  line-height: 1.5em;
-}
-
-h3 {
-  color: #456;
-  font-size: 20px;
-  font-weight: normal;
-  line-height: 28px;
-}
-
-hr {
-  margin: 18px 0;
-  border: 0;
-  border-top: 1px solid #EEE;
-  border-bottom: 1px solid white;
-}
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 55851befc8cb362c880912d345e5a5f49d902c6f..186239d3096b6a548b41d8bf0348e446986f3380 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -30,44 +30,4 @@ describe ApplicationController do
       controller.send(:check_password_expiration)
     end
   end
-
-  describe 'check labels authorization' do
-    let(:project) { create(:project) }
-    let(:user) { create(:user) }
-    let(:controller) { ApplicationController.new }
-
-    before do
-      project.team << [user, :guest]
-      allow(controller).to receive(:current_user).and_return(user)
-      allow(controller).to receive(:project).and_return(project)
-    end
-
-    it 'should succeed if issues and MRs are enabled' do
-      project.issues_enabled = true
-      project.merge_requests_enabled = true
-      controller.send(:authorize_read_label!)
-      expect(response.status).to eq(200)
-    end
-
-    it 'should succeed if issues are enabled, MRs are disabled' do
-      project.issues_enabled = true
-      project.merge_requests_enabled = false
-      controller.send(:authorize_read_label!)
-      expect(response.status).to eq(200)
-    end
-
-    it 'should succeed if issues are disabled, MRs are enabled' do
-      project.issues_enabled = false
-      project.merge_requests_enabled = true
-      controller.send(:authorize_read_label!)
-      expect(response.status).to eq(200)
-    end
-
-    it 'should fail if issues and MRs are disabled' do
-      project.issues_enabled = false
-      project.merge_requests_enabled = false
-      expect(controller).to receive(:access_denied!)
-      controller.send(:authorize_read_label!)
-    end
-  end
 end
diff --git a/spec/controllers/groups/avatars_controller_spec.rb b/spec/controllers/groups/avatars_controller_spec.rb
index 3dac134a73180aa5c46546cb0f7f9d708bfb04e5..91d639218e59e263e378fb9ecca081ec5b23229d 100644
--- a/spec/controllers/groups/avatars_controller_spec.rb
+++ b/spec/controllers/groups/avatars_controller_spec.rb
@@ -2,9 +2,10 @@ require 'spec_helper'
 
 describe Groups::AvatarsController do
   let(:user)  { create(:user) }
-  let(:group) { create(:group, owner: user, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
+  let(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
 
   before do
+    group.add_owner(user)
     sign_in(user)
   end
 
diff --git a/spec/controllers/namespaces_controller_spec.rb b/spec/controllers/namespaces_controller_spec.rb
index 774369587113bca181d9b6fdedbfb737731e465b..27e9afe582e806f30d5147056e99bbe2e2d06ad0 100644
--- a/spec/controllers/namespaces_controller_spec.rb
+++ b/spec/controllers/namespaces_controller_spec.rb
@@ -15,14 +15,9 @@ describe NamespacesController do
     end
 
     context "when the namespace belongs to a group" do
-      let!(:group) { create(:group) }
-      let!(:project) { create(:project, namespace: group) }
-
-      context "when the group has public projects" do
-        before do
-          project.update_attribute(:visibility_level, Project::PUBLIC)
-        end
+      let!(:group)   { create(:group) }
 
+      context "when the group is public" do
         context "when not signed in" do
           it "redirects to the group's page" do
             get :show, id: group.path
@@ -44,27 +39,31 @@ describe NamespacesController do
         end
       end
 
-      context "when the project doesn't have public projects" do
+      context "when the group is private" do
+        before do
+          group.update_attribute(:visibility_level, Group::PRIVATE)
+        end
+
         context "when not signed in" do
-          it "does not redirect to the sign in page" do
+          it "redirects to the sign in page" do
             get :show, id: group.path
-            expect(response).not_to redirect_to(new_user_session_path)
+            expect(response).to redirect_to(new_user_session_path)
           end
         end
+
         context "when signed in" do
           before do
             sign_in(user)
           end
 
-          context "when the user has access to the project" do
+          context "when the user has access to the group" do
             before do
-              project.team << [user, :master]
+              group.add_developer(user)
             end
 
             context "when the user is blocked" do
               before do
                 user.block
-                project.team << [user, :master]
               end
 
               it "redirects to the sign in page" do
@@ -83,11 +82,11 @@ describe NamespacesController do
             end
           end
 
-          context "when the user doesn't have access to the project" do
-            it "redirects to the group's page" do
+          context "when the user doesn't have access to the group" do
+            it "responds with status 404" do
               get :show, id: group.path
 
-              expect(response).to redirect_to(group_path(group))
+              expect(response.status).to eq(404)
             end
           end
         end
diff --git a/spec/controllers/projects/avatars_controller_spec.rb b/spec/controllers/projects/avatars_controller_spec.rb
index e79b46a3504c877dd2df7ff24f589580eb84f996..4d724ca9ed0175624b18a7460df5487147493bb1 100644
--- a/spec/controllers/projects/avatars_controller_spec.rb
+++ b/spec/controllers/projects/avatars_controller_spec.rb
@@ -6,7 +6,7 @@ describe Projects::AvatarsController do
 
   before do
     sign_in(user)
-    project.team << [user, :developer]
+    project.team << [user, :master]
     controller.instance_variable_set(:@project, project)
   end
 
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 76d56bc989d97507a399a8aa60ba96122164f194..d6e4cd71ce6b11233b18f130142cf62b41b96283 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -1,16 +1,16 @@
 require('spec_helper')
 
 describe Projects::IssuesController do
-  let(:project) { create(:project) }
+  let(:project) { create(:project_empty_repo) }
   let(:user)    { create(:user) }
-  let(:issue) { create(:issue, project: project) }
-
-  before do
-    sign_in(user)
-    project.team << [user, :developer]
-  end
+  let(:issue)   { create(:issue, project: project) }
 
   describe "GET #index" do
+    before do
+      sign_in(user)
+      project.team << [user, :developer]
+    end
+
     it "returns index" do
       get :index, namespace_id: project.namespace.path, project_id: project.path
 
@@ -38,6 +38,177 @@ describe Projects::IssuesController do
       get :index, namespace_id: project.namespace.path, project_id: project.path
       expect(response.status).to eq(404)
     end
+  end
+
+  describe 'Confidential Issues' do
+    let(:project) { create(:project_empty_repo, :public) }
+    let(:assignee) { create(:assignee) }
+    let(:author) { create(:user) }
+    let(:non_member) { create(:user) }
+    let(:member) { create(:user) }
+    let(:admin) { create(:admin) }
+    let!(:issue) { create(:issue, project: project) }
+    let!(:unescaped_parameter_value) { create(:issue, :confidential, project: project, author: author) }
+    let!(:request_forgery_timing_attack) { create(:issue, :confidential, project: project, assignee: assignee) }
+
+    describe 'GET #index' do
+      it 'should not list confidential issues for guests' do
+        sign_out(:user)
+        get_issues
+
+        expect(assigns(:issues)).to eq [issue]
+      end
+
+      it 'should not list confidential issues for non project members' do
+        sign_in(non_member)
+        get_issues
+
+        expect(assigns(:issues)).to eq [issue]
+      end
+
+      it 'should list confidential issues for author' do
+        sign_in(author)
+        get_issues
+
+        expect(assigns(:issues)).to include unescaped_parameter_value
+        expect(assigns(:issues)).not_to include request_forgery_timing_attack
+      end
+
+      it 'should list confidential issues for assignee' do
+        sign_in(assignee)
+        get_issues
+
+        expect(assigns(:issues)).not_to include unescaped_parameter_value
+        expect(assigns(:issues)).to include request_forgery_timing_attack
+      end
+
+      it 'should list confidential issues for project members' do
+        sign_in(member)
+        project.team << [member, :developer]
+
+        get_issues
+
+        expect(assigns(:issues)).to include unescaped_parameter_value
+        expect(assigns(:issues)).to include request_forgery_timing_attack
+      end
+
+      it 'should list confidential issues for admin' do
+        sign_in(admin)
+        get_issues
+
+        expect(assigns(:issues)).to include unescaped_parameter_value
+        expect(assigns(:issues)).to include request_forgery_timing_attack
+      end
+
+      def get_issues
+        get :index,
+          namespace_id: project.namespace.to_param,
+          project_id: project.to_param
+      end
+    end
+
+    shared_examples_for 'restricted action' do |http_status|
+      it 'returns 404 for guests' do
+        sign_out :user
+        go(id: unescaped_parameter_value.to_param)
+
+        expect(response).to have_http_status :not_found
+      end
+
+      it 'returns 404 for non project members' do
+        sign_in(non_member)
+        go(id: unescaped_parameter_value.to_param)
+
+        expect(response).to have_http_status :not_found
+      end
+
+      it "returns #{http_status[:success]} for author" do
+        sign_in(author)
+        go(id: unescaped_parameter_value.to_param)
+
+        expect(response).to have_http_status http_status[:success]
+      end
+
+      it "returns #{http_status[:success]} for assignee" do
+        sign_in(assignee)
+        go(id: request_forgery_timing_attack.to_param)
+
+        expect(response).to have_http_status http_status[:success]
+      end
+
+      it "returns #{http_status[:success]} for project members" do
+        sign_in(member)
+        project.team << [member, :developer]
+        go(id: unescaped_parameter_value.to_param)
+
+        expect(response).to have_http_status http_status[:success]
+      end
+
+      it "returns #{http_status[:success]} for admin" do
+        sign_in(admin)
+        go(id: unescaped_parameter_value.to_param)
+
+        expect(response).to have_http_status http_status[:success]
+      end
+    end
+
+    describe 'GET #show' do
+      it_behaves_like 'restricted action', success: 200
+
+      def go(id:)
+        get :show,
+          namespace_id: project.namespace.to_param,
+          project_id: project.to_param,
+          id: id
+      end
+    end
+
+    describe 'GET #edit' do
+      it_behaves_like 'restricted action', success: 200
 
+      def go(id:)
+        get :edit,
+          namespace_id: project.namespace.to_param,
+          project_id: project.to_param,
+          id: id
+      end
+    end
+
+    describe 'PUT #update' do
+      it_behaves_like 'restricted action', success: 302
+
+      def go(id:)
+        put :update,
+          namespace_id: project.namespace.to_param,
+          project_id: project.to_param,
+          id: id,
+          issue: { title: 'New title' }
+      end
+    end
+  end
+
+  describe "DELETE #destroy" do
+    context "when the user is a developer" do
+      before { sign_in(user) }
+      it "rejects a developer to destroy an issue" do
+        delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid
+        expect(response.status).to eq(404)
+      end
+    end
+
+    context "when the user is owner" do
+      let(:owner)     { create(:user) }
+      let(:namespace) { create(:namespace, owner: owner) }
+      let(:project)   { create(:project, namespace: namespace) }
+
+      before { sign_in(owner) }
+
+      it "deletes the issue" do
+        delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid
+
+        expect(response.status).to eq(302)
+        expect(controller).to set_flash[:notice].to(/The issue was successfully deleted\./).now
+      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 e82fe26c7a62e701ca30de46d81fd0081ad17298..c5b034dc0648bbb403a1080ce855cd05ff4fb670 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -157,6 +157,29 @@ describe Projects::MergeRequestsController do
     end
   end
 
+  describe "DELETE #destroy" do
+    it "denies access to users unless they're admin or project owner" do
+      delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid
+
+      expect(response.status).to eq(404)
+    end
+
+    context "when the user is owner" do
+      let(:owner)     { create(:user) }
+      let(:namespace) { create(:namespace, owner: owner) }
+      let(:project)   { create(:project, namespace: namespace) }
+
+      before { sign_in owner }
+
+      it "deletes the merge request" do
+        delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid
+
+        expect(response.status).to eq(302)
+        expect(controller).to set_flash[:notice].to(/The merge request was successfully deleted\./).now
+      end
+    end
+  end
+
   describe 'GET diffs' do
     def go(format: 'html')
       get :diffs,
diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb
index af5d043cf02276ead874f5b16e0a309edce94772..73858e6f0637de5c7127798abe757a9e4c42e601 100644
--- a/spec/controllers/uploads_controller_spec.rb
+++ b/spec/controllers/uploads_controller_spec.rb
@@ -30,7 +30,7 @@ describe UploadsController do
           end
         end
       end
-      
+
       context "when not signed in" do
         it "responds with status 200" do
           get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png"
@@ -126,14 +126,9 @@ describe UploadsController do
     end
 
     context "when viewing a group avatar" do
-      let!(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
-      let!(:project) { create(:project, namespace: group) }
-
-      context "when the group has public projects" do
-        before do
-          project.update_attribute(:visibility_level, Project::PUBLIC)
-        end
+      let!(:group)   { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
 
+      context "when the group is public" do
         context "when not signed in" do
           it "responds with status 200" do
             get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png"
@@ -155,7 +150,11 @@ describe UploadsController do
         end
       end
 
-      context "when the project doesn't have public projects" do
+      context "when the group is private" do
+        before do
+          group.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PRIVATE)
+        end
+
         context "when signed in" do
           before do
             sign_in(user)
@@ -163,13 +162,12 @@ describe UploadsController do
 
           context "when the user has access to the project" do
             before do
-              project.team << [user, :master]
+              group.add_developer(user)
             end
 
             context "when the user is blocked" do
               before do
                 user.block
-                project.team << [user, :master]
               end
 
               it "redirects to the sign in page" do
diff --git a/spec/factories/broadcast_messages.rb b/spec/factories/broadcast_messages.rb
index 373ca75467e07dbcd3427406d426d9f79c8e6150..c80e736655198165f1cb300b3161322593df201c 100644
--- a/spec/factories/broadcast_messages.rb
+++ b/spec/factories/broadcast_messages.rb
@@ -15,7 +15,7 @@
 FactoryGirl.define do
   factory :broadcast_message do
     message "MyText"
-    starts_at Date.today
+    starts_at Date.yesterday
     ends_at Date.tomorrow
 
     trait :expired do
diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb
index 4a3a155d7ff50882e3a6e4fd1bd4a161c933718f..2d47a6f6c4cb0dc222c97cf4400fdb82379b96f6 100644
--- a/spec/factories/groups.rb
+++ b/spec/factories/groups.rb
@@ -3,5 +3,17 @@ FactoryGirl.define do
     sequence(:name) { |n| "group#{n}" }
     path { name.downcase.gsub(/\s/, '_') }
     type 'Group'
+
+    trait :public do
+      visibility_level Gitlab::VisibilityLevel::PUBLIC
+    end
+
+    trait :internal do
+      visibility_level Gitlab::VisibilityLevel::INTERNAL
+    end
+
+    trait :private do
+      visibility_level Gitlab::VisibilityLevel::PRIVATE
+    end
   end
 end
diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb
index 722095de5905efa1f2937a14862ef391008590c8..e72aa9479b757973db6330557595826e4e17a9c7 100644
--- a/spec/factories/issues.rb
+++ b/spec/factories/issues.rb
@@ -4,6 +4,10 @@ FactoryGirl.define do
     author
     project
 
+    trait :confidential do
+      confidential true
+    end
+
     trait :closed do
       state :closed
     end
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index a9df5fa1d3a8572cdfc6debdd25d9a112f0bc2da..e281e2f227b6cb0ddcf487d19bf35af5c3907c3f 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -51,6 +51,11 @@ FactoryGirl.define do
     trait :with_diffs do
     end
 
+    trait :without_diffs do
+      source_branch "improve/awesome"
+      target_branch "master"
+    end
+
     trait :conflict do
       source_branch "feature_conflict"
       target_branch "feature"
diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb
index bd85b1d798af4047b0d28c40662fce8c86abbc70..7ae06c278408caae9fd352c6db57df80086a2bd7 100644
--- a/spec/factories/todos.rb
+++ b/spec/factories/todos.rb
@@ -5,14 +5,15 @@
 #  id          :integer          not null, primary key
 #  user_id     :integer          not null
 #  project_id  :integer          not null
-#  target_id   :integer          not null
+#  target_id   :integer
 #  target_type :string           not null
 #  author_id   :integer
-#  note_id     :integer
 #  action      :integer          not null
 #  state       :string           not null
 #  created_at  :datetime
 #  updated_at  :datetime
+#  note_id     :integer
+#  commit_id   :string
 #
 
 FactoryGirl.define do
@@ -30,5 +31,10 @@ FactoryGirl.define do
     trait :mentioned do
       action { Todo::MENTIONED }
     end
+
+    trait :on_commit do
+      commit_id RepoHelpers.sample_commit.id
+      target_type "Commit"
+    end
   end
 end
diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6fda0c3186674c06b1d660e3e5d0421e18a35e57
--- /dev/null
+++ b/spec/features/issues/move_spec.rb
@@ -0,0 +1,87 @@
+require 'rails_helper'
+
+feature 'issue move to another project' do
+  let(:user) { create(:user) }
+  let(:old_project) { create(:project) }
+  let(:text) { 'Some issue description' }
+
+  let(:issue) do
+    create(:issue, description: text, project: old_project, author: user)
+  end
+
+  background { login_as(user) }
+
+  context 'user does not have permission to move issue' do
+    background do
+      old_project.team << [user, :guest]
+
+      edit_issue(issue)
+    end
+
+    scenario 'moving issue to another project not allowed' do
+      expect(page).to have_no_select('move_to_project_id')
+    end
+  end
+
+  context 'user has permission to move issue' do
+    let!(:mr) { create(:merge_request, source_project: old_project) }
+    let(:new_project) { create(:project) }
+    let(:text) { 'Text with !1' }
+    let(:cross_reference) { old_project.to_reference }
+
+    background do
+      old_project.team << [user, :reporter]
+      new_project.team << [user, :reporter]
+
+      edit_issue(issue)
+    end
+
+    scenario 'moving issue to another project' do
+      select(new_project.name_with_namespace, from: 'move_to_project_id')
+      click_button('Save changes')
+
+      expect(current_url).to include project_path(new_project)
+
+      page.within('.issue') do
+        expect(page).to have_content("Text with #{cross_reference}!1")
+        expect(page).to have_content("Moved from #{cross_reference}#1")
+        expect(page).to have_content(issue.title)
+      end
+    end
+
+    context 'projects user does not have permission to move issue to exist' do
+      let!(:private_project) { create(:project, :private) }
+      let(:another_project) { create(:project) }
+      background { another_project.team << [user, :guest] }
+
+      scenario 'browsing projects in projects select' do
+        options = [ '', 'No project', new_project.name_with_namespace ]
+        expect(page).to have_select('move_to_project_id', options: options)
+      end
+    end
+
+    context 'issue has been already moved' do
+      let(:new_issue) { create(:issue, project: new_project) }
+      let(:issue) do
+        create(:issue, project: old_project, author: user, moved_to: new_issue)
+      end
+
+      scenario 'user wants to move issue that has already been moved' do
+        expect(page).to have_no_select('move_to_project_id')
+      end
+    end
+  end
+
+  def edit_issue(issue)
+    visit issue_path(issue)
+    page.within('.issuable-header') { click_link 'Edit' }
+  end
+
+  def issue_path(issue)
+    namespace_project_issue_path(issue.project.namespace, issue.project, issue)
+  end
+
+  def project_path(project)
+    namespace_project_path(new_project.namespace, new_project)
+  end
+end
diff --git a/spec/features/issues/new_branch_button_spec.rb b/spec/features/issues/new_branch_button_spec.rb
index 1f3bd915f48291fa32da7d3556ce04f5dd52050d..9219b767547782a9c795dbb7da98fc8a5c6a8686 100644
--- a/spec/features/issues/new_branch_button_spec.rb
+++ b/spec/features/issues/new_branch_button_spec.rb
@@ -24,7 +24,7 @@ feature 'Start new branch from an issue', feature: true do
       end
       let(:referenced_mr) do
         create(:merge_request, :simple, source_project: project, target_project: project,
-                                        description: "Fixes ##{issue.iid}")
+                                        description: "Fixes ##{issue.iid}", author: user)
       end
 
       before do
diff --git a/spec/features/merge_requests/filter_by_milestone_spec.rb b/spec/features/merge_requests/filter_by_milestone_spec.rb
index 1b2fd1bab1086bd22b9e7e6c2d25364477cc37e3..b76e4c74c79656a4b1e11436858c7e791590a6ce 100644
--- a/spec/features/merge_requests/filter_by_milestone_spec.rb
+++ b/spec/features/merge_requests/filter_by_milestone_spec.rb
@@ -30,8 +30,6 @@ feature 'Merge Request filtering by Milestone', feature: true do
 
   def filter_by_milestone(title)
     find(".js-milestone-select").click
-    sleep 0.5
     find(".milestone-filter a", text: title).click
-    sleep 1
   end
 end
diff --git a/spec/features/security/group/internal_access_spec.rb b/spec/features/security/group/internal_access_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..71b783b7276ad1992c0dd8229d4e7f4dd896aa46
--- /dev/null
+++ b/spec/features/security/group/internal_access_spec.rb
@@ -0,0 +1,109 @@
+require 'rails_helper'
+
+describe 'Internal Group access', feature: true do
+  include AccessMatchers
+
+  let(:group) { create(:group, :internal) }
+  let(:project) { create(:project, :internal, group: group) }
+
+  let(:owner)     { create(:user) }
+  let(:master)    { create(:user) }
+  let(:developer) { create(:user) }
+  let(:reporter)  { create(:user) }
+  let(:guest)     { create(:user) }
+
+  let(:project_guest) { create(:user) }
+
+  before do
+    group.add_owner(owner)
+    group.add_master(master)
+    group.add_developer(developer)
+    group.add_reporter(reporter)
+    group.add_guest(guest)
+
+    project.team << [project_guest, :guest]
+  end
+
+  describe "Group should be internal" do
+    describe '#internal?' do
+      subject { group.internal? }
+      it { is_expected.to be_truthy }
+    end
+  end
+
+  describe 'GET /groups/:path' do
+    subject { group_path(group) }
+
+    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 project_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
+
+  describe 'GET /groups/:path/issues' do
+    subject { issues_group_path(group) }
+
+    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 project_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
+
+  describe 'GET /groups/:path/merge_requests' do
+    subject { merge_requests_group_path(group) }
+
+    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 project_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
+
+
+  describe 'GET /groups/:path/group_members' do
+    subject { group_group_members_path(group) }
+
+    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 project_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
+
+  describe 'GET /groups/:path/edit' do
+    subject { edit_group_path(group) }
+
+    it { is_expected.to be_allowed_for :admin }
+    it { is_expected.to be_allowed_for owner }
+    it { is_expected.to be_denied_for master }
+    it { is_expected.to be_denied_for developer }
+    it { is_expected.to be_denied_for reporter }
+    it { is_expected.to be_denied_for guest }
+    it { is_expected.to be_denied_for project_guest }
+    it { is_expected.to be_denied_for :user }
+    it { is_expected.to be_denied_for :visitor }
+    it { is_expected.to be_denied_for :external }
+  end
+end
diff --git a/spec/features/security/group/private_access_spec.rb b/spec/features/security/group/private_access_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cc9aee802f98b96877035cbefe97176a41c7a568
--- /dev/null
+++ b/spec/features/security/group/private_access_spec.rb
@@ -0,0 +1,109 @@
+require 'rails_helper'
+
+describe 'Private Group access', feature: true do
+  include AccessMatchers
+
+  let(:group) { create(:group, :private) }
+  let(:project) { create(:project, :private, group: group) }
+
+  let(:owner)     { create(:user) }
+  let(:master)    { create(:user) }
+  let(:developer) { create(:user) }
+  let(:reporter)  { create(:user) }
+  let(:guest)     { create(:user) }
+
+  let(:project_guest) { create(:user) }
+
+  before do
+    group.add_owner(owner)
+    group.add_master(master)
+    group.add_developer(developer)
+    group.add_reporter(reporter)
+    group.add_guest(guest)
+
+    project.team << [project_guest, :guest]
+  end
+
+  describe "Group should be private" do
+    describe '#private?' do
+      subject { group.private? }
+      it { is_expected.to be_truthy }
+    end
+  end
+
+  describe 'GET /groups/:path' do
+    subject { group_path(group) }
+
+    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 project_guest }
+    it { is_expected.to be_denied_for :user }
+    it { is_expected.to be_denied_for :external }
+    it { is_expected.to be_denied_for :visitor }
+  end
+
+  describe 'GET /groups/:path/issues' do
+    subject { issues_group_path(group) }
+
+    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 project_guest }
+    it { is_expected.to be_denied_for :user }
+    it { is_expected.to be_denied_for :external }
+    it { is_expected.to be_denied_for :visitor }
+  end
+
+  describe 'GET /groups/:path/merge_requests' do
+    subject { merge_requests_group_path(group) }
+
+    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 project_guest }
+    it { is_expected.to be_denied_for :user }
+    it { is_expected.to be_denied_for :external }
+    it { is_expected.to be_denied_for :visitor }
+  end
+
+
+  describe 'GET /groups/:path/group_members' do
+    subject { group_group_members_path(group) }
+
+    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 project_guest }
+    it { is_expected.to be_denied_for :user }
+    it { is_expected.to be_denied_for :external }
+    it { is_expected.to be_denied_for :visitor }
+  end
+
+  describe 'GET /groups/:path/edit' do
+    subject { edit_group_path(group) }
+
+    it { is_expected.to be_allowed_for :admin }
+    it { is_expected.to be_allowed_for owner }
+    it { is_expected.to be_denied_for master }
+    it { is_expected.to be_denied_for developer }
+    it { is_expected.to be_denied_for reporter }
+    it { is_expected.to be_denied_for guest }
+    it { is_expected.to be_denied_for project_guest }
+    it { is_expected.to be_denied_for :user }
+    it { is_expected.to be_denied_for :visitor }
+    it { is_expected.to be_denied_for :external }
+  end
+end
diff --git a/spec/features/security/group/public_access_spec.rb b/spec/features/security/group/public_access_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..db986683dbeaa42141f8edb5ebff346a0fd204d6
--- /dev/null
+++ b/spec/features/security/group/public_access_spec.rb
@@ -0,0 +1,109 @@
+require 'rails_helper'
+
+describe 'Public Group access', feature: true do
+  include AccessMatchers
+
+  let(:group) { create(:group, :public) }
+  let(:project) { create(:project, :public, group: group) }
+
+  let(:owner)     { create(:user) }
+  let(:master)    { create(:user) }
+  let(:developer) { create(:user) }
+  let(:reporter)  { create(:user) }
+  let(:guest)     { create(:user) }
+
+  let(:project_guest) { create(:user) }
+
+  before do
+    group.add_owner(owner)
+    group.add_master(master)
+    group.add_developer(developer)
+    group.add_reporter(reporter)
+    group.add_guest(guest)
+    
+    project.team << [project_guest, :guest]
+  end
+
+  describe "Group should be public" do
+    describe '#public?' do
+      subject { group.public? }
+      it { is_expected.to be_truthy }
+    end
+  end
+
+  describe 'GET /groups/:path' do
+    subject { group_path(group) }
+
+    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 project_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
+
+  describe 'GET /groups/:path/issues' do
+    subject { issues_group_path(group) }
+
+    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 project_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
+
+  describe 'GET /groups/:path/merge_requests' do
+    subject { merge_requests_group_path(group) }
+
+    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 project_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
+
+
+  describe 'GET /groups/:path/group_members' do
+    subject { group_group_members_path(group) }
+
+    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 project_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
+
+  describe 'GET /groups/:path/edit' do
+    subject { edit_group_path(group) }
+
+    it { is_expected.to be_allowed_for :admin }
+    it { is_expected.to be_allowed_for owner }
+    it { is_expected.to be_denied_for master }
+    it { is_expected.to be_denied_for developer }
+    it { is_expected.to be_denied_for reporter }
+    it { is_expected.to be_denied_for guest }
+    it { is_expected.to be_denied_for project_guest }
+    it { is_expected.to be_denied_for :user }
+    it { is_expected.to be_denied_for :visitor }
+    it { is_expected.to be_denied_for :external }
+  end
+end
diff --git a/spec/features/security/group_access_spec.rb b/spec/features/security/group_access_spec.rb
deleted file mode 100644
index 65f8073c6933937f2264d17103af85671ab64951..0000000000000000000000000000000000000000
--- a/spec/features/security/group_access_spec.rb
+++ /dev/null
@@ -1,284 +0,0 @@
-require 'rails_helper'
-
-describe 'Group access', feature: true do
-  include AccessMatchers
-
-  def group
-    @group ||= create(:group)
-  end
-
-  def create_project(access_level)
-    if access_level == :mixed
-      create(:empty_project, :public, group: group)
-      create(:empty_project, :internal, group: group)
-    else
-      create(:empty_project, access_level, group: group)
-    end
-  end
-
-  def group_member(access_level, grp = group())
-    level = Object.const_get("Gitlab::Access::#{access_level.upcase}")
-
-    create(:user).tap do |user|
-      grp.add_user(user, level)
-    end
-  end
-
-  describe 'GET /groups/new' do
-    subject { new_group_path }
-
-    it { is_expected.to be_allowed_for :admin }
-    it { is_expected.to be_allowed_for :user }
-    it { is_expected.to be_denied_for :visitor }
-  end
-
-  describe 'GET /groups/:path' do
-    subject { group_path(group) }
-
-    context 'with public projects' do
-      let!(:project) { create_project(:public) }
-
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_allowed_for group_member(:master) }
-      it { is_expected.to be_allowed_for group_member(:reporter) }
-      it { is_expected.to be_allowed_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_allowed_for :user }
-      it { is_expected.to be_allowed_for :visitor }
-    end
-
-    context 'with mixed projects' do
-      let!(:project) { create_project(:mixed) }
-
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_allowed_for group_member(:master) }
-      it { is_expected.to be_allowed_for group_member(:reporter) }
-      it { is_expected.to be_allowed_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_allowed_for :user }
-      it { is_expected.to be_allowed_for :visitor }
-    end
-
-    context 'with internal projects' do
-      let!(:project) { create_project(:internal) }
-
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_allowed_for group_member(:master) }
-      it { is_expected.to be_allowed_for group_member(:reporter) }
-      it { is_expected.to be_allowed_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_allowed_for :user }
-      it { is_expected.to be_allowed_for :visitor }
-    end
-
-    context 'with no projects' do
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_allowed_for group_member(:master) }
-      it { is_expected.to be_allowed_for group_member(:reporter) }
-      it { is_expected.to be_allowed_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_allowed_for :user }
-      it { is_expected.to be_allowed_for :visitor }
-    end
-  end
-
-  describe 'GET /groups/:path/issues' do
-    subject { issues_group_path(group) }
-
-    context 'with public projects' do
-      let!(:project) { create_project(:public) }
-
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_allowed_for group_member(:master) }
-      it { is_expected.to be_allowed_for group_member(:reporter) }
-      it { is_expected.to be_allowed_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_allowed_for :user }
-      it { is_expected.to be_allowed_for :visitor }
-    end
-
-    context 'with mixed projects' do
-      let!(:project) { create_project(:mixed) }
-
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_allowed_for group_member(:master) }
-      it { is_expected.to be_allowed_for group_member(:reporter) }
-      it { is_expected.to be_allowed_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_allowed_for :user }
-      it { is_expected.to be_allowed_for :visitor }
-    end
-
-    context 'with internal projects' do
-      let!(:project) { create_project(:internal) }
-
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_allowed_for group_member(:master) }
-      it { is_expected.to be_allowed_for group_member(:reporter) }
-      it { is_expected.to be_allowed_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_allowed_for :user }
-      it { is_expected.to be_denied_for :visitor }
-    end
-
-    context 'with no projects' do
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_allowed_for group_member(:master) }
-      it { is_expected.to be_allowed_for group_member(:reporter) }
-      it { is_expected.to be_allowed_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_denied_for :user }
-      it { is_expected.to be_denied_for :visitor }
-    end
-  end
-
-  describe 'GET /groups/:path/merge_requests' do
-    subject { merge_requests_group_path(group) }
-
-    context 'with public projects' do
-      let!(:project) { create_project(:public) }
-
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_allowed_for group_member(:master) }
-      it { is_expected.to be_allowed_for group_member(:reporter) }
-      it { is_expected.to be_allowed_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_allowed_for :user }
-      it { is_expected.to be_allowed_for :visitor }
-    end
-
-    context 'with mixed projects' do
-      let!(:project) { create_project(:mixed) }
-
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_allowed_for group_member(:master) }
-      it { is_expected.to be_allowed_for group_member(:reporter) }
-      it { is_expected.to be_allowed_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_allowed_for :user }
-      it { is_expected.to be_allowed_for :visitor }
-    end
-
-    context 'with internal projects' do
-      let!(:project) { create_project(:internal) }
-
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_allowed_for group_member(:master) }
-      it { is_expected.to be_allowed_for group_member(:reporter) }
-      it { is_expected.to be_allowed_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_allowed_for :user }
-      it { is_expected.to be_denied_for :visitor }
-    end
-
-    context 'with no projects' do
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_allowed_for group_member(:master) }
-      it { is_expected.to be_allowed_for group_member(:reporter) }
-      it { is_expected.to be_allowed_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_denied_for :user }
-      it { is_expected.to be_denied_for :visitor }
-    end
-  end
-
-  describe 'GET /groups/:path/group_members' do
-    subject { group_group_members_path(group) }
-
-    context 'with public projects' do
-      let!(:project) { create_project(:public) }
-
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_allowed_for group_member(:master) }
-      it { is_expected.to be_allowed_for group_member(:reporter) }
-      it { is_expected.to be_allowed_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_allowed_for :user }
-      it { is_expected.to be_allowed_for :visitor }
-    end
-
-    context 'with mixed projects' do
-      let!(:project) { create_project(:mixed) }
-
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_allowed_for group_member(:master) }
-      it { is_expected.to be_allowed_for group_member(:reporter) }
-      it { is_expected.to be_allowed_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_allowed_for :user }
-      it { is_expected.to be_allowed_for :visitor }
-    end
-
-    context 'with internal projects' do
-      let!(:project) { create_project(:internal) }
-
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_allowed_for group_member(:master) }
-      it { is_expected.to be_allowed_for group_member(:reporter) }
-      it { is_expected.to be_allowed_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_allowed_for :user }
-      it { is_expected.to be_denied_for :visitor }
-    end
-
-    context 'with no projects' do
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_allowed_for group_member(:master) }
-      it { is_expected.to be_allowed_for group_member(:reporter) }
-      it { is_expected.to be_allowed_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_denied_for :user }
-      it { is_expected.to be_denied_for :visitor }
-    end
-  end
-
-  describe 'GET /groups/:path/edit' do
-    subject { edit_group_path(group) }
-
-    context 'with public projects' do
-      let!(:project) { create_project(:public) }
-
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_denied_for group_member(:master) }
-      it { is_expected.to be_denied_for group_member(:reporter) }
-      it { is_expected.to be_denied_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_denied_for :user }
-      it { is_expected.to be_denied_for :visitor }
-    end
-
-    context 'with mixed projects' do
-      let!(:project) { create_project(:mixed) }
-
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_denied_for group_member(:master) }
-      it { is_expected.to be_denied_for group_member(:reporter) }
-      it { is_expected.to be_denied_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_denied_for :user }
-      it { is_expected.to be_denied_for :visitor }
-    end
-
-    context 'with internal projects' do
-      let!(:project) { create_project(:internal) }
-
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_denied_for group_member(:master) }
-      it { is_expected.to be_denied_for group_member(:reporter) }
-      it { is_expected.to be_denied_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_denied_for :user }
-      it { is_expected.to be_denied_for :visitor }
-    end
-
-    context 'with no projects' do
-      it { is_expected.to be_allowed_for group_member(:owner) }
-      it { is_expected.to be_denied_for group_member(:master) }
-      it { is_expected.to be_denied_for group_member(:reporter) }
-      it { is_expected.to be_denied_for group_member(:guest) }
-      it { is_expected.to be_allowed_for :admin }
-      it { is_expected.to be_denied_for :user }
-      it { is_expected.to be_denied_for :visitor }
-    end
-  end
-end
diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb
index f88c591d897fa7973295528fc233b2f6e24019d2..79d5bf4cf06cc892927234ae43c395978daae4be 100644
--- a/spec/features/security/project/internal_access_spec.rb
+++ b/spec/features/security/project/internal_access_spec.rb
@@ -5,25 +5,22 @@ describe "Internal Project Access", feature: true  do
 
   let(:project) { create(:project, :internal) }
 
-  let(:master) { create(:user) }
-  let(:guest) { create(:user) }
-  let(:reporter) { create(:user) }
-  let(:external_team_member) { create(:user, external: true) }
+  let(:owner)     { project.owner }
+  let(:master)    { create(:user) }
+  let(:developer) { create(:user) }
+  let(:reporter)  { create(:user) }
+  let(:guest)     { create(:user) }
 
   before do
-    # full access
     project.team << [master, :master]
-    project.team << [external_team_member, :master]
-
-    # readonly
+    project.team << [developer, :developer]
     project.team << [reporter, :reporter]
+    project.team << [guest, :guest]
   end
 
   describe "Project should be internal" do
-    subject { project }
-
     describe '#internal?' do
-      subject { super().internal? }
+      subject { project.internal? }
       it { is_expected.to be_truthy }
     end
   end
@@ -31,78 +28,84 @@ describe "Internal Project Access", feature: true  do
   describe "GET /:project_path" do
     subject { namespace_project_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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/tree/master" do
     subject { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) }
 
+    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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/commits/master" do
     subject { namespace_project_commits_path(project.namespace, project, project.repository.root_ref, limit: 1) }
 
+    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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/commit/:sha" do
     subject { namespace_project_commit_path(project.namespace, project, project.repository.commit) }
 
+    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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/compare" do
     subject { namespace_project_compare_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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/project_members" do
     subject { namespace_project_project_members_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_denied_for developer }
     it { is_expected.to be_denied_for reporter }
-    it { is_expected.to be_allowed_for :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
@@ -110,52 +113,56 @@ describe "Internal Project Access", feature: true  do
     let(:commit) { project.repository.commit }
     subject { namespace_project_blob_path(project.namespace, project, File.join(commit.id, '.gitignore')) }
 
+    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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/edit" do
     subject { edit_namespace_project_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_denied_for developer }
     it { is_expected.to be_denied_for reporter }
-    it { is_expected.to be_allowed_for :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/deploy_keys" do
     subject { namespace_project_deploy_keys_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_denied_for developer }
     it { is_expected.to be_denied_for reporter }
-    it { is_expected.to be_allowed_for :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/issues" do
     subject { namespace_project_issues_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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
@@ -163,65 +170,70 @@ describe "Internal Project Access", feature: true  do
     let(:issue) { create(:issue, project: project) }
     subject { edit_namespace_project_issue_path(project.namespace, project, issue) }
 
+    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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/snippets" do
     subject { namespace_project_snippets_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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/snippets/new" do
     subject { new_namespace_project_snippet_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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/merge_requests" do
     subject { namespace_project_merge_requests_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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/merge_requests/new" do
     subject { new_namespace_project_merge_request_path(project.namespace, project) }
 
+    it { is_expected.to be_allowed_for :admin }
+    it { is_expected.to be_allowed_for owner }
     it { is_expected.to be_allowed_for master }
+    it { is_expected.to be_allowed_for developer }
     it { is_expected.to be_denied_for reporter }
-    it { is_expected.to be_allowed_for :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
@@ -233,13 +245,14 @@ describe "Internal Project Access", feature: true  do
       allow_any_instance_of(Project).to receive(:branches).and_return([])
     end
 
+    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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
@@ -251,26 +264,28 @@ describe "Internal Project Access", feature: true  do
       allow_any_instance_of(Project).to receive(:tags).and_return([])
     end
 
+    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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/hooks" do
     subject { namespace_project_hooks_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_denied_for developer }
     it { is_expected.to be_denied_for reporter }
-    it { is_expected.to be_allowed_for :admin }
     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_allowed_for external_team_member }
     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 19f287ce7a47d7e0c5f3a2378654dd0c1b0fec46..0a89193eb67d554c3f3beaa7bbaf47211c5a785d 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -3,27 +3,24 @@ require 'spec_helper'
 describe "Private Project Access", feature: true  do
   include AccessMatchers
 
-  let(:project) { create(:project) }
+  let(:project) { create(:project, :private) }
 
-  let(:master)   { create(:user) }
-  let(:guest)    { create(:user) }
-  let(:reporter) { create(:user) }
-  let(:external_team_member) { create(:user, external: true) }
+  let(:owner)     { project.owner }
+  let(:master)    { create(:user) }
+  let(:developer) { create(:user) }
+  let(:reporter)  { create(:user) }
+  let(:guest)     { create(:user) }
 
   before do
-    # full access
     project.team << [master, :master]
-    project.team << [external_team_member, :master]
-
-    # readonly
+    project.team << [developer, :developer]
     project.team << [reporter, :reporter]
+    project.team << [guest, :guest]
   end
 
   describe "Project should be private" do
-    subject { project }
-
     describe '#private?' do
-      subject { super().private? }
+      subject { project.private? }
       it { is_expected.to be_truthy }
     end
   end
@@ -31,77 +28,84 @@ describe "Private Project Access", feature: true  do
   describe "GET /:project_path" do
     subject { namespace_project_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 :admin }
-    it { is_expected.to be_denied_for guest }
+    it { is_expected.to be_allowed_for guest }
     it { is_expected.to be_denied_for :user }
     it { is_expected.to be_denied_for :external }
-    it { is_expected.to be_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/tree/master" do
     subject { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) }
 
+    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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/commits/master" do
     subject { namespace_project_commits_path(project.namespace, project, project.repository.root_ref, limit: 1) }
 
+    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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/commit/:sha" do
     subject { namespace_project_commit_path(project.namespace, project, project.repository.commit) }
 
+    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 :admin }
     it { is_expected.to be_denied_for guest }
     it { is_expected.to be_denied_for :user }
-    it { is_expected.to be_allowed_for external_team_member }
+    it { is_expected.to be_denied_for :external }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/compare" do
     subject { namespace_project_compare_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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/project_members" do
     subject { namespace_project_project_members_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_denied_for developer }
     it { is_expected.to be_denied_for reporter }
-    it { is_expected.to be_allowed_for :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
@@ -109,52 +113,56 @@ describe "Private Project Access", feature: true  do
     let(:commit) { project.repository.commit }
     subject { namespace_project_blob_path(project.namespace, project, File.join(commit.id, '.gitignore'))}
 
+    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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/edit" do
     subject { edit_namespace_project_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_denied_for developer }
     it { is_expected.to be_denied_for reporter }
-    it { is_expected.to be_allowed_for :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/deploy_keys" do
     subject { namespace_project_deploy_keys_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_denied_for developer }
     it { is_expected.to be_denied_for reporter }
-    it { is_expected.to be_allowed_for :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/issues" do
     subject { namespace_project_issues_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 :admin }
-    it { is_expected.to be_denied_for guest }
+    it { is_expected.to be_allowed_for guest }
     it { is_expected.to be_denied_for :user }
     it { is_expected.to be_denied_for :external }
-    it { is_expected.to be_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
@@ -162,39 +170,42 @@ describe "Private Project Access", feature: true  do
     let(:issue) { create(:issue, project: project) }
     subject { edit_namespace_project_issue_path(project.namespace, project, issue) }
 
+    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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/snippets" do
     subject { namespace_project_snippets_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 :admin }
-    it { is_expected.to be_denied_for guest }
+    it { is_expected.to be_allowed_for guest }
     it { is_expected.to be_denied_for :user }
     it { is_expected.to be_denied_for :external }
-    it { is_expected.to be_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/merge_requests" do
     subject { namespace_project_merge_requests_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 :admin }
-    it { is_expected.to be_denied_for guest }
+    it { is_expected.to be_allowed_for guest }
     it { is_expected.to be_denied_for :user }
     it { is_expected.to be_denied_for :external }
-    it { is_expected.to be_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
@@ -206,13 +217,14 @@ describe "Private Project Access", feature: true  do
       allow_any_instance_of(Project).to receive(:branches).and_return([])
     end
 
+    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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
@@ -224,26 +236,28 @@ describe "Private Project Access", feature: true  do
       allow_any_instance_of(Project).to receive(:tags).and_return([])
     end
 
+    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 :admin }
     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_allowed_for external_team_member }
     it { is_expected.to be_denied_for :visitor }
   end
 
   describe "GET /:project_path/hooks" do
     subject { namespace_project_hooks_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_denied_for developer }
     it { is_expected.to be_denied_for reporter }
-    it { is_expected.to be_allowed_for :admin }
     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_allowed_for external_team_member }
     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 4e13507636751484ff360fcc5733c00217d1351c..40daac89d40f22be676864edd3d5e9079d7b6438 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -3,29 +3,24 @@ require 'spec_helper'
 describe "Public Project Access", feature: true  do
   include AccessMatchers
 
-  let(:project) { create(:project) }
+  let(:project) { create(:project, :public) }
 
-  let(:master) { create(:user) }
-  let(:guest) { create(:user) }
-  let(:reporter) { create(:user) }
+  let(:owner)     { project.owner }
+  let(:master)    { create(:user) }
+  let(:developer) { create(:user) }
+  let(:reporter)  { create(:user) }
+  let(:guest)     { create(:user) }
 
   before do
-    # public project
-    project.visibility_level = Gitlab::VisibilityLevel::PUBLIC
-    project.save!
-
-    # full access
     project.team << [master, :master]
-
-    # readonly
+    project.team << [developer, :developer]
     project.team << [reporter, :reporter]
+    project.team << [guest, :guest]
   end
 
   describe "Project should be public" do
-    subject { project }
-
     describe '#public?' do
-      subject { super().public? }
+      subject { project.public? }
       it { is_expected.to be_truthy }
     end
   end
@@ -33,9 +28,11 @@ describe "Public Project Access", feature: true  do
   describe "GET /:project_path" do
     subject { namespace_project_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 :admin }
     it { is_expected.to be_allowed_for guest }
     it { is_expected.to be_allowed_for :user }
     it { is_expected.to be_allowed_for :external }
@@ -45,9 +42,11 @@ describe "Public Project Access", feature: true  do
   describe "GET /:project_path/tree/master" do
     subject { namespace_project_tree_path(project.namespace, project, project.repository.root_ref) }
 
+    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 :admin }
     it { is_expected.to be_allowed_for guest }
     it { is_expected.to be_allowed_for :user }
     it { is_expected.to be_allowed_for :external }
@@ -57,9 +56,11 @@ describe "Public Project Access", feature: true  do
   describe "GET /:project_path/commits/master" do
     subject { namespace_project_commits_path(project.namespace, project, project.repository.root_ref, limit: 1) }
 
+    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 :admin }
     it { is_expected.to be_allowed_for guest }
     it { is_expected.to be_allowed_for :user }
     it { is_expected.to be_allowed_for :external }
@@ -69,9 +70,11 @@ describe "Public Project Access", feature: true  do
   describe "GET /:project_path/commit/:sha" do
     subject { namespace_project_commit_path(project.namespace, project, project.repository.commit) }
 
+    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 :admin }
     it { is_expected.to be_allowed_for guest }
     it { is_expected.to be_allowed_for :user }
     it { is_expected.to be_allowed_for :external }
@@ -81,9 +84,11 @@ describe "Public Project Access", feature: true  do
   describe "GET /:project_path/compare" do
     subject { namespace_project_compare_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 :admin }
     it { is_expected.to be_allowed_for guest }
     it { is_expected.to be_allowed_for :user }
     it { is_expected.to be_allowed_for :external }
@@ -93,9 +98,11 @@ describe "Public Project Access", feature: true  do
   describe "GET /:project_path/project_members" do
     subject { namespace_project_project_members_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_denied_for developer }
     it { is_expected.to be_denied_for reporter }
-    it { is_expected.to be_allowed_for :admin }
     it { is_expected.to be_denied_for guest }
     it { is_expected.to be_denied_for :user }
     it { is_expected.to be_denied_for :external }
@@ -108,9 +115,11 @@ describe "Public Project Access", feature: true  do
     context "when allowed for public" do
       before { project.update(public_builds: true) }
 
+      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 :admin }
       it { is_expected.to be_allowed_for guest }
       it { is_expected.to be_allowed_for :user }
       it { is_expected.to be_allowed_for :external }
@@ -120,9 +129,11 @@ describe "Public Project Access", feature: true  do
     context "when disallowed for public" do
       before { project.update(public_builds: false) }
 
+      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 :admin }
       it { is_expected.to be_denied_for guest }
       it { is_expected.to be_denied_for :user }
       it { is_expected.to be_denied_for :external }
@@ -138,9 +149,11 @@ describe "Public Project Access", feature: true  do
     context "when allowed for public" do
       before { project.update(public_builds: true) }
 
+      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 :admin }
       it { is_expected.to be_allowed_for guest }
       it { is_expected.to be_allowed_for :user }
       it { is_expected.to be_allowed_for :external }
@@ -150,9 +163,11 @@ describe "Public Project Access", feature: true  do
     context "when disallowed for public" do
       before { project.update(public_builds: false) }
 
+      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 :admin }
       it { is_expected.to be_denied_for guest }
       it { is_expected.to be_denied_for :user }
       it { is_expected.to be_denied_for :external }
@@ -165,9 +180,11 @@ describe "Public Project Access", feature: true  do
 
     subject { namespace_project_blob_path(project.namespace, project, File.join(commit.id, '.gitignore')) }
 
+    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 :admin }
     it { is_expected.to be_allowed_for guest }
     it { is_expected.to be_allowed_for :user }
     it { is_expected.to be_allowed_for :visitor }
@@ -176,9 +193,11 @@ describe "Public Project Access", feature: true  do
   describe "GET /:project_path/edit" do
     subject { edit_namespace_project_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_denied_for developer }
     it { is_expected.to be_denied_for reporter }
-    it { is_expected.to be_allowed_for :admin }
     it { is_expected.to be_denied_for guest }
     it { is_expected.to be_denied_for :user }
     it { is_expected.to be_denied_for :external }
@@ -188,9 +207,11 @@ describe "Public Project Access", feature: true  do
   describe "GET /:project_path/deploy_keys" do
     subject { namespace_project_deploy_keys_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_denied_for developer }
     it { is_expected.to be_denied_for reporter }
-    it { is_expected.to be_allowed_for :admin }
     it { is_expected.to be_denied_for guest }
     it { is_expected.to be_denied_for :user }
     it { is_expected.to be_denied_for :external }
@@ -200,9 +221,11 @@ describe "Public Project Access", feature: true  do
   describe "GET /:project_path/issues" do
     subject { namespace_project_issues_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 :admin }
     it { is_expected.to be_allowed_for guest }
     it { is_expected.to be_allowed_for :user }
     it { is_expected.to be_allowed_for :external }
@@ -213,9 +236,11 @@ describe "Public Project Access", feature: true  do
     let(:issue) { create(:issue, project: project) }
     subject { edit_namespace_project_issue_path(project.namespace, project, issue) }
 
+    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 :admin }
     it { is_expected.to be_denied_for guest }
     it { is_expected.to be_denied_for :user }
     it { is_expected.to be_denied_for :external }
@@ -225,9 +250,11 @@ describe "Public Project Access", feature: true  do
   describe "GET /:project_path/snippets" do
     subject { namespace_project_snippets_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 :admin }
     it { is_expected.to be_allowed_for guest }
     it { is_expected.to be_allowed_for :user }
     it { is_expected.to be_allowed_for :external }
@@ -237,9 +264,11 @@ describe "Public Project Access", feature: true  do
   describe "GET /:project_path/snippets/new" do
     subject { new_namespace_project_snippet_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 :admin }
     it { is_expected.to be_denied_for guest }
     it { is_expected.to be_denied_for :user }
     it { is_expected.to be_denied_for :external }
@@ -249,9 +278,11 @@ describe "Public Project Access", feature: true  do
   describe "GET /:project_path/merge_requests" do
     subject { namespace_project_merge_requests_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 :admin }
     it { is_expected.to be_allowed_for guest }
     it { is_expected.to be_allowed_for :user }
     it { is_expected.to be_allowed_for :external }
@@ -261,9 +292,11 @@ describe "Public Project Access", feature: true  do
   describe "GET /:project_path/merge_requests/new" do
     subject { new_namespace_project_merge_request_path(project.namespace, project) }
 
+    it { is_expected.to be_allowed_for :admin }
+    it { is_expected.to be_allowed_for owner }
     it { is_expected.to be_allowed_for master }
+    it { is_expected.to be_allowed_for developer }
     it { is_expected.to be_denied_for reporter }
-    it { is_expected.to be_allowed_for :admin }
     it { is_expected.to be_denied_for guest }
     it { is_expected.to be_denied_for :user }
     it { is_expected.to be_denied_for :external }
@@ -278,9 +311,11 @@ describe "Public Project Access", feature: true  do
       allow_any_instance_of(Project).to receive(:branches).and_return([])
     end
 
+    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 :admin }
     it { is_expected.to be_allowed_for guest }
     it { is_expected.to be_allowed_for :user }
     it { is_expected.to be_allowed_for :external }
@@ -295,9 +330,11 @@ describe "Public Project Access", feature: true  do
       allow_any_instance_of(Project).to receive(:tags).and_return([])
     end
 
+    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 :admin }
     it { is_expected.to be_allowed_for guest }
     it { is_expected.to be_allowed_for :user }
     it { is_expected.to be_allowed_for :external }
@@ -307,9 +344,11 @@ describe "Public Project Access", feature: true  do
   describe "GET /:project_path/hooks" do
     subject { namespace_project_hooks_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_denied_for developer }
     it { is_expected.to be_denied_for reporter }
-    it { is_expected.to be_allowed_for :admin }
     it { is_expected.to be_denied_for guest }
     it { is_expected.to be_denied_for :user }
     it { is_expected.to be_denied_for :external }
diff --git a/spec/finders/group_projects_finder_spec.rb b/spec/finders/group_projects_finder_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fdd3849816fae4a79d00f43b21b347be7c29861f
--- /dev/null
+++ b/spec/finders/group_projects_finder_spec.rb
@@ -0,0 +1,89 @@
+require 'spec_helper'
+
+describe GroupProjectsFinder do
+  let(:group) { create(:group) }
+  let(:current_user) { create(:user) }
+
+  let(:finder) { described_class.new(source_user) }
+
+  let!(:public_project) { create(:project, :public, group: group, path: '1') }
+  let!(:private_project) { create(:project, :private, group: group, path: '2') }
+  let!(:shared_project_1) { create(:project, :public, path: '3') }
+  let!(:shared_project_2) { create(:project, :private, path: '4') }
+  let!(:shared_project_3) { create(:project, :internal, path: '5') }
+
+
+  before do
+    shared_project_1.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group)
+    shared_project_2.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group)
+    shared_project_3.project_group_links.create(group_access: Gitlab::Access::MASTER, group: group)
+  end
+
+
+  describe 'with a group member current user' do
+    before  { group.add_user(current_user, Gitlab::Access::MASTER) }
+
+    context "only shared" do
+      subject { described_class.new(group, only_shared: true).execute(current_user) }
+      it      { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1]) }
+    end
+
+    context "only owned" do
+      subject { described_class.new(group, only_owned: true).execute(current_user) }
+      it      { is_expected.to eq([private_project, public_project]) }
+    end
+
+    context "all" do
+      subject { described_class.new(group).execute(current_user) }
+      it      { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1, private_project, public_project]) }
+    end
+  end
+
+  describe 'without group member current_user' do
+    before { shared_project_2.team << [current_user, Gitlab::Access::MASTER] }
+
+    context "only shared" do
+      context "without external user" do
+        subject { described_class.new(group, only_shared: true).execute(current_user) }
+        it      { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1]) }
+      end
+
+      context "with external user" do
+        before  { current_user.update_attributes(external: true) }
+        subject { described_class.new(group, only_shared: true).execute(current_user) }
+        it      { is_expected.to eq([shared_project_2, shared_project_1]) }
+      end
+    end
+
+    context "only owned" do
+      context "without external user" do
+        before  { private_project.team << [current_user, Gitlab::Access::MASTER] }
+        subject { described_class.new(group, only_owned: true).execute(current_user) }
+        it      { is_expected.to eq([private_project, public_project]) }
+      end
+
+      context "with external user" do
+        before  { current_user.update_attributes(external: true) }
+        subject { described_class.new(group, only_owned: true).execute(current_user) }
+        it      { is_expected.to eq([public_project]) }
+      end
+
+      context "all" do
+        subject { described_class.new(group).execute(current_user) }
+        it      { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1, public_project]) }
+      end
+    end
+  end
+
+  describe "no user" do
+    context "only shared" do
+      subject { described_class.new(group, only_shared: true).execute(current_user) }
+      it      { is_expected.to eq([shared_project_3, shared_project_1]) }
+    end
+
+    context "only owned" do
+      subject { described_class.new(group, only_owned: true).execute(current_user) }
+      it      { is_expected.to eq([public_project]) }
+    end
+  end
+end
diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d5d111e8d15be73ef1b8497d31bf1930089ff965
--- /dev/null
+++ b/spec/finders/groups_finder_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe GroupsFinder do
+  describe '#execute' do
+    let(:user)            { create(:user) }
+    let!(:private_group)  { create(:group, :private) }
+    let!(:internal_group) { create(:group, :internal) }
+    let!(:public_group)   { create(:group, :public) }
+    let(:finder)          { described_class.new }
+
+    describe 'execute' do
+      describe 'without a user' do
+        subject { finder.execute }
+
+        it { is_expected.to eq([public_group]) }
+      end
+
+      describe 'with a user' do
+        subject { finder.execute(user) }
+
+        context 'normal user' do
+          it { is_expected.to eq([public_group, internal_group]) }
+        end
+
+        context 'external user' do
+          let(:user) { create(:user, external: true) }
+
+          it { is_expected.to eq([public_group]) }
+        end
+      end
+    end
+  end
+end
diff --git a/spec/finders/joined_groups_finder_spec.rb b/spec/finders/joined_groups_finder_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f90a8e007c8e10b3dc212ba7dc2358e1d4929e1f
--- /dev/null
+++ b/spec/finders/joined_groups_finder_spec.rb
@@ -0,0 +1,77 @@
+require 'spec_helper'
+
+describe JoinedGroupsFinder do
+  describe '#execute' do
+    let!(:profile_owner)    { create(:user) }
+    let!(:profile_visitor)  { create(:user) }
+
+    let!(:private_group)    { create(:group, :private) }
+    let!(:private_group_2)  { create(:group, :private) }
+    let!(:internal_group)   { create(:group, :internal) }
+    let!(:internal_group_2) { create(:group, :internal) }
+    let!(:public_group)     { create(:group, :public) }
+    let!(:public_group_2)   { create(:group, :public) }
+    let!(:finder) { described_class.new(profile_owner) }
+
+    context 'without a user' do
+      before do
+        public_group.add_master(profile_owner)
+      end
+
+      it 'only shows public groups from profile owner' do
+        expect(finder.execute).to eq([public_group])
+      end
+    end
+
+    context "with a user" do
+      before do
+        private_group.add_master(profile_owner)
+        internal_group.add_master(profile_owner)
+        public_group.add_master(profile_owner)
+      end
+
+      context "when the profile visitor is in the private group" do
+        before do
+          private_group.add_developer(profile_visitor)
+        end
+
+        it 'only shows groups where both users are authorized to see' do
+          expect(finder.execute(profile_visitor)).to eq([public_group, internal_group, private_group])
+        end
+      end
+
+      context 'if profile visitor is in one of the private group projects' do
+        before do
+          project = create(:project, :private, group: private_group, name: 'B', path: 'B')
+          project.team.add_user(profile_visitor, Gitlab::Access::DEVELOPER)
+        end
+
+        it 'shows group' do
+          expect(finder.execute(profile_visitor)).to eq([public_group, internal_group, private_group])
+        end
+      end
+
+      context 'external users' do
+        before do
+          profile_visitor.update_attributes(external: true)
+        end
+
+        context 'if not a member' do
+          it "does not show internal groups" do
+            expect(finder.execute(profile_visitor)).to eq([public_group])
+          end
+        end
+
+        context "if authorized" do
+          before do
+            internal_group.add_master(profile_visitor)
+          end
+
+          it "shows internal groups if authorized" do
+            expect(finder.execute(profile_visitor)).to eq([public_group, internal_group])
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/spec/finders/personal_projects_finder_spec.rb b/spec/finders/personal_projects_finder_spec.rb
index 38817add45684906a556bdcaec283486345f446a..a4681fe59d899bcd75c8a6ccf7c9f1692d61743a 100644
--- a/spec/finders/personal_projects_finder_spec.rb
+++ b/spec/finders/personal_projects_finder_spec.rb
@@ -1,19 +1,17 @@
 require 'spec_helper'
 
 describe PersonalProjectsFinder do
-  let(:source_user) { create(:user) }
-  let(:current_user) { create(:user) }
+  let(:source_user)     { create(:user) }
+  let(:current_user)    { create(:user) }
+  let(:finder)          { described_class.new(source_user) }
+  let!(:public_project) { create(:project, :public, namespace: source_user.namespace) }
 
-  let(:finder) { described_class.new(source_user) }
-
-  let!(:public_project) do
-    create(:project, :public, namespace: source_user.namespace, name: 'A',
-                              path: 'A')
+  let!(:private_project) do
+    create(:project, :private, namespace: source_user.namespace, path: 'mepmep')
   end
 
-  let!(:private_project) do
-    create(:project, :private, namespace: source_user.namespace, name: 'B',
-                               path: 'B')
+  let!(:internal_project) do
+    create(:project, :internal, namespace: source_user.namespace, path: 'C')
   end
 
   before do
@@ -29,6 +27,14 @@ describe PersonalProjectsFinder do
   describe 'with a current user' do
     subject { finder.execute(current_user) }
 
-    it { is_expected.to eq([private_project, public_project]) }
+    context 'normal user' do
+      it { is_expected.to eq([internal_project, private_project, public_project]) }
+    end
+
+    context 'external' do
+      before { current_user.update_attributes(external: true) }
+
+      it { is_expected.to eq([private_project, public_project]) }
+    end
   end
 end
diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb
index fae0da9d8980c2a666a6d08ac9e08fc6a5190f45..0a1cc3b3df7dedc6601ae75c1f48da9162b02a6b 100644
--- a/spec/finders/projects_finder_spec.rb
+++ b/spec/finders/projects_finder_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
 describe ProjectsFinder do
   describe '#execute' do
     let(:user) { create(:user) }
-    let(:group) { create(:group) }
+    let(:group) { create(:group, :public) }
 
     let!(:private_project) do
       create(:project, :private, name: 'A', path: 'A')
diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb
index 7fdc5e5d7aab0ba5c322363d18cc18db69acd167..810016c96583e5fe9bc1e7f4aa678d74f99ab483 100644
--- a/spec/finders/snippets_finder_spec.rb
+++ b/spec/finders/snippets_finder_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
 describe SnippetsFinder do
   let(:user) { create :user }
   let(:user1) { create :user }
-  let(:group) { create :group }
+  let(:group) { create :group, :public }
 
   let(:project1) { create(:empty_project, :public,  group: group) }
   let(:project2) { create(:empty_project, :private, group: group) }
diff --git a/spec/helpers/groups_helper.rb b/spec/helpers/groups_helper_spec.rb
similarity index 100%
rename from spec/helpers/groups_helper.rb
rename to spec/helpers/groups_helper_spec.rb
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index 4f129eca183f85a42f0732101231477ff7ee2b35..39042ff7e91653f4086394f90db5075d15099165 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -11,7 +11,7 @@ describe LabelsHelper do
       end
 
       it 'uses the instance variable' do
-        expect(link_to_label(label)).to match %r{<a href="/#{@project.to_reference}/issues\?label_name=#{label.name}">.*</a>}
+        expect(link_to_label(label)).to match %r{<a href="/#{@project.to_reference}/issues\?label_name=#{label.name}"><span class="[\w\s\-]*has-tooltip".*</span></a>}
       end
     end
 
@@ -39,6 +39,14 @@ describe LabelsHelper do
       end
     end
 
+    context 'with a tooltip argument' do
+      context 'set to false' do
+        it 'does not include the has-tooltip class' do
+          expect(link_to_label(label, tooltip: false)).not_to match %r{has-tooltip}
+        end
+      end
+    end
+
     context 'with block' do
       it 'passes the block to link_to' do
         link = link_to_label(label) { 'Foo' }
@@ -49,7 +57,7 @@ describe LabelsHelper do
     context 'without block' do
       it 'uses render_colored_label as the link content' do
         expect(self).to receive(:render_colored_label).
-          with(label).and_return('Foo')
+          with(label, tooltip: true).and_return('Foo')
         expect(link_to_label(label)).to match('Foo')
       end
     end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 53207767581e906005768c4e3313ea0beca51b35..c258cfebd732c0bc77c1b700586720f6ebbd3fcd 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -11,16 +11,8 @@ describe ProjectsHelper do
 
   describe "can_change_visibility_level?" do
     let(:project) { create(:project) }
-
-    let(:fork_project) do
-      fork_project = create(:forked_project_with_submodules)
-      fork_project.build_forked_project_link(forked_to_project_id: fork_project.id, forked_from_project_id: project.id)
-      fork_project.save
-
-      fork_project
-    end
-
     let(:user) { create(:user) }
+    let(:fork_project) { Projects::ForkService.new(project, user).execute }
 
     it "returns false if there are no appropriate permissions" do
       allow(helper).to receive(:can?) { false }
@@ -94,4 +86,23 @@ describe ProjectsHelper do
       end
     end
   end
+
+  describe 'default_clone_protocol' do
+    describe 'using HTTP' do
+      it 'returns HTTP' do
+        expect(helper).to receive(:current_user).and_return(nil)
+
+        expect(helper.send(:default_clone_protocol)).to eq('http')
+      end
+    end
+
+    describe 'using HTTPS' do
+      it 'returns HTTPS' do
+        allow(Gitlab.config.gitlab).to receive(:protocol).and_return('https')
+        expect(helper).to receive(:current_user).and_return(nil)
+
+        expect(helper.send(:default_clone_protocol)).to eq('https')
+      end
+    end
+  end
 end
diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb
index cd7596a763d9930c37afc812700b391fdd554017..ff98249570d1074349c4c4d632bcdf55db7ce4b1 100644
--- a/spec/helpers/visibility_level_helper_spec.rb
+++ b/spec/helpers/visibility_level_helper_spec.rb
@@ -8,6 +8,7 @@ describe VisibilityLevelHelper do
   end
 
   let(:project)          { build(:project) }
+  let(:group)            { build(:group) }
   let(:personal_snippet) { build(:personal_snippet) }
   let(:project_snippet)  { build(:project_snippet) }
 
@@ -19,6 +20,13 @@ describe VisibilityLevelHelper do
       end
     end
 
+    context 'used with a Group' do
+      it 'delegates groups to #group_visibility_level_description' do
+        expect(visibility_level_description(Gitlab::VisibilityLevel::PRIVATE, group))
+            .to match /group/i
+      end
+    end
+
     context 'called with a Snippet' do
       it 'delegates snippets to #snippet_visibility_level_description' do
         expect(visibility_level_description(Gitlab::VisibilityLevel::INTERNAL, project_snippet))
@@ -58,13 +66,8 @@ describe VisibilityLevelHelper do
 
   describe "skip_level?" do
     describe "forks" do
-      let(:project) { create(:project, :internal) }
-      let(:fork_project) { create(:forked_project_with_submodules) }
-
-      before do
-        fork_project.build_forked_project_link(forked_to_project_id: fork_project.id, forked_from_project_id: project.id)
-        fork_project.save
-      end
+      let(:project)       { create(:project, :internal) }
+      let(:fork_project)  { create(:project, forked_from_project: project) }
 
       it "skips levels" do
         expect(skip_level?(fork_project, Gitlab::VisibilityLevel::PUBLIC)).to be_truthy
diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index e2d21f53b7e327177f9ae8d2f0603608beefc3b6..94468abcbb3c197265bff809a4d52732b0d468d4 100644
--- a/spec/lib/banzai/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -56,7 +56,7 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
   describe 'label span element' do
     it 'includes default classes' do
       doc = reference_filter("Label #{reference}")
-      expect(doc.css('a span').first.attr('class')).to eq 'label color-label'
+      expect(doc.css('a span').first.attr('class')).to eq 'label color-label has-tooltip'
     end
 
     it 'includes a style attribute' do
diff --git a/spec/lib/banzai/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb
index e9bb388e361b44743f791ad087411799049df26b..c2c2fd0eb6ac3403663268d66ab9fb0b941f87bb 100644
--- a/spec/lib/banzai/filter/redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/redactor_filter_spec.rb
@@ -44,12 +44,82 @@ describe Banzai::Filter::RedactorFilter, lib: true do
     end
   end
 
-  context "for user references" do
+  context 'with data-issue' do
+    context 'for confidential issues' do
+      it 'removes references for non project members' do
+        non_member = create(:user)
+        project = create(:empty_project, :public)
+        issue = create(:issue, :confidential, project: project)
+
+        link = reference_link(project: project.id, issue: issue.id, reference_filter: 'IssueReferenceFilter')
+        doc = filter(link, current_user: non_member)
+
+        expect(doc.css('a').length).to eq 0
+      end
+
+      it 'allows references for author' do
+        author = create(:user)
+        project = create(:empty_project, :public)
+        issue = create(:issue, :confidential, project: project, author: author)
+
+        link = reference_link(project: project.id, issue: issue.id, reference_filter: 'IssueReferenceFilter')
+        doc = filter(link, current_user: author)
+
+        expect(doc.css('a').length).to eq 1
+      end
+
+      it 'allows references for assignee' do
+        assignee = create(:user)
+        project = create(:empty_project, :public)
+        issue = create(:issue, :confidential, project: project, assignee: assignee)
+
+        link = reference_link(project: project.id, issue: issue.id, reference_filter: 'IssueReferenceFilter')
+        doc = filter(link, current_user: assignee)
 
+        expect(doc.css('a').length).to eq 1
+      end
+
+      it 'allows references for project members' do
+        member = create(:user)
+        project = create(:empty_project, :public)
+        project.team << [member, :developer]
+        issue = create(:issue, :confidential, project: project)
+
+        link = reference_link(project: project.id, issue: issue.id, reference_filter: 'IssueReferenceFilter')
+        doc = filter(link, current_user: member)
+
+        expect(doc.css('a').length).to eq 1
+      end
+
+      it 'allows references for admin' do
+        admin = create(:admin)
+        project = create(:empty_project, :public)
+        issue = create(:issue, :confidential, project: project)
+
+        link = reference_link(project: project.id, issue: issue.id, reference_filter: 'IssueReferenceFilter')
+        doc = filter(link, current_user: admin)
+
+        expect(doc.css('a').length).to eq 1
+      end
+    end
+
+    it 'allows references for non confidential issues' do
+      user = create(:user)
+      project = create(:empty_project, :public)
+      issue = create(:issue, project: project)
+
+      link = reference_link(project: project.id, issue: issue.id, reference_filter: 'IssueReferenceFilter')
+      doc = filter(link, current_user: user)
+
+      expect(doc.css('a').length).to eq 1
+    end
+  end
+
+  context "for user references" do
     context 'with data-group' do
       it 'removes unpermitted Group references' do
         user = create(:user)
-        group = create(:group)
+        group = create(:group, :private)
 
         link = reference_link(group: group.id, reference_filter: 'UserReferenceFilter')
         doc = filter(link, current_user: user)
@@ -59,7 +129,7 @@ describe Banzai::Filter::RedactorFilter, lib: true do
 
       it 'allows permitted Group references' do
         user = create(:user)
-        group = create(:group)
+        group = create(:group, :private)
         group.add_developer(user)
 
         link = reference_link(group: group.id, reference_filter: 'UserReferenceFilter')
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index fab6412d29fad1d48f17ddd3d60913a1ca42fcea..b79b8147ce0d11996f2c4f2b5ac14b75d446ef8e 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -97,6 +97,28 @@ module Ci
           expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
         end
 
+        it "returns builds if only has a triggers keyword specified and a trigger is provided" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", type: type, only: ["triggers"] }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
+
+          expect(config_processor.builds_for_stage_and_ref(type, "deploy", false, true).size).to eq(1)
+        end
+
+        it "does not return builds if only has a triggers keyword specified and no trigger is provided" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", type: type, only: ["triggers"] }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
+
+          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
+        end
+
         it "returns builds if only has current repository path" do
           config = YAML.dump({
                                before_script: ["pwd"],
@@ -203,6 +225,28 @@ module Ci
           expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
         end
 
+        it "does not return builds if except has a triggers keyword specified and a trigger is provided" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", type: type, except: ["triggers"] }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
+
+          expect(config_processor.builds_for_stage_and_ref(type, "deploy", false, true).size).to eq(0)
+        end
+
+        it "returns builds if except has a triggers keyword specified and no trigger is provided" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", type: type, except: ["triggers"] }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
+
+          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
+        end
+
         it "does not return builds if except has current repository path" do
           config = YAML.dump({
                                before_script: ["pwd"],
diff --git a/spec/lib/ci/status_spec.rb b/spec/lib/ci/status_spec.rb
index 1539720bb8dd300de1316c4bd6d0d79f00e8cc83..47f3df6e3ce008ec265c683b9544fd097b453a6d 100644
--- a/spec/lib/ci/status_spec.rb
+++ b/spec/lib/ci/status_spec.rb
@@ -48,6 +48,29 @@ describe Ci::Status do
         it { is_expected.to eq 'success' }
       end
 
+      context 'success and canceled' do
+        let(:statuses) do
+          [create(type, status: :success), create(type, status: :canceled)]
+        end
+        it { is_expected.to eq 'failed' }
+      end
+
+      context 'all canceled' do
+        let(:statuses) do
+          [create(type, status: :canceled), create(type, status: :canceled)]
+        end
+        it { is_expected.to eq 'canceled' }
+      end
+
+      context 'success and canceled but allowed to fail' do
+        let(:statuses) do
+          [create(type, status: :success),
+           create(type, status: :canceled, allow_failure: true)]
+        end
+
+        it { is_expected.to eq 'success' }
+      end
+
       context 'one finished and second running but allowed to fail' do
         let(:statuses) do
           [create(type, status: :success),
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index 04cf11fc6f17525039eaa017a8bbf071f7f99d91..844fd79c991af8b69f1be01d296f51c4e1c302ca 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -11,6 +11,7 @@ describe Gitlab::ClosingIssueExtractor, lib: true do
   subject { described_class.new(project, project.creator) }
 
   before do
+    project.team  << [project.creator, :developer]
     project2.team << [project.creator, :master]
   end
 
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index 0d9694f2c1304d44023ac55ee33bc3e11d79282e..a0cbef6e6a441d830c474d37cc0c00712616f39d 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -18,4 +18,18 @@ describe Gitlab::Diff::File, lib: true do
   describe :mode_changed? do
     it { expect(diff_file.mode_changed?).to be_falsey }
   end
+
+  describe '#too_large?' do
+    it 'returns true for a file that is too large' do
+      expect(diff).to receive(:too_large?).and_return(true)
+
+      expect(diff_file.too_large?).to eq(true)
+    end
+
+    it 'returns false for a file that is small enough' do
+      expect(diff).to receive(:too_large?).and_return(false)
+
+      expect(diff_file.too_large?).to eq(false)
+    end
+  end
 end
diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0a7ca3ec848be174fe4e7418957b74fa3c8ca6d3
--- /dev/null
+++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
@@ -0,0 +1,89 @@
+require 'spec_helper'
+
+describe Gitlab::Gfm::ReferenceRewriter do
+  let(:text) { 'some text' }
+  let(:old_project) { create(:project) }
+  let(:new_project) { create(:project) }
+  let(:user) { create(:user) }
+
+  before { old_project.team << [user, :guest] }
+
+  describe '#rewrite' do
+    subject do
+      described_class.new(text, old_project, user).rewrite(new_project)
+    end
+
+    context 'multiple issues and merge requests referenced' do
+      let!(:issue_first) { create(:issue, project: old_project) }
+      let!(:issue_second) { create(:issue, project: old_project) }
+      let!(:merge_request) { create(:merge_request, source_project: old_project) }
+
+      context 'plain text description' do
+        let(:text) { 'Description that references #1, #2 and !1' }
+
+        it { is_expected.to include issue_first.to_reference(new_project) }
+        it { is_expected.to include issue_second.to_reference(new_project) }
+        it { is_expected.to include merge_request.to_reference(new_project) }
+      end
+
+      context 'description with ignored elements' do
+        let(:text) do
+          "Hi. This references #1, but not `#2`\n" +
+          '<pre>and not !1</pre>'
+        end
+
+        it { is_expected.to include issue_first.to_reference(new_project) }
+        it { is_expected.to_not include issue_second.to_reference(new_project) }
+        it { is_expected.to_not include merge_request.to_reference(new_project) }
+      end
+
+      context 'description ambigous elements' do
+        context 'url' do
+          let(:url) { 'http://gitlab.com/#1' }
+          let(:text) { "This references #1, but not #{url}" }
+
+          it { is_expected.to include url }
+        end
+
+        context 'code' do
+          let(:text) { "#1, but not `[#1]`" }
+          it { is_expected.to eq "#{issue_first.to_reference(new_project)}, but not `[#1]`" }
+        end
+
+        context 'code reverse' do
+          let(:text) { "not `#1`, but #1" }
+          it { is_expected.to eq "not `#1`, but #{issue_first.to_reference(new_project)}" }
+        end
+
+        context 'code in random order' do
+          let(:text) { "#1, `#1`, #1, `#1`" }
+          let(:ref) { issue_first.to_reference(new_project) }
+
+          it { is_expected.to eq "#{ref}, `#1`, #{ref}, `#1`" }
+        end
+
+        context 'description with labels' do
+          let!(:label) { create(:label, id: 123, name: 'test', project: old_project) }
+          let(:project_ref) { old_project.to_reference }
+
+          context 'label referenced by id' do
+            let(:text) { '#1 and ~123' }
+            it { is_expected.to eq %Q{#{project_ref}#1 and #{project_ref}~123} }
+          end
+
+          context 'label referenced by text' do
+            let(:text) { '#1 and ~"test"' }
+            it { is_expected.to eq %Q{#{project_ref}#1 and #{project_ref}~123} }
+          end
+        end
+      end
+
+      context 'reference contains milestone' do
+        let(:milestone) { create(:milestone) }
+        let(:text) { "milestone ref: #{milestone.to_reference}" }
+
+        it { is_expected.to eq text }
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index 09adbc07dcbbdb1c7f5bc58a5ca9526d3430d472..db0ff95b4f5c6c83be75122b2ad560f05dea14a4 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -1,11 +1,12 @@
 require 'spec_helper'
 
 describe Gitlab::ProjectSearchResults, lib: true do
+  let(:user) { create(:user) }
   let(:project) { create(:project) }
   let(:query) { 'hello world' }
 
   describe 'initialize with empty ref' do
-    let(:results) { Gitlab::ProjectSearchResults.new(project, query, '') }
+    let(:results) { Gitlab::ProjectSearchResults.new(user, project, query, '') }
 
     it { expect(results.project).to eq(project) }
     it { expect(results.repository_ref).to be_nil }
@@ -14,10 +15,74 @@ describe Gitlab::ProjectSearchResults, lib: true do
 
   describe 'initialize with ref' do
     let(:ref) { 'refs/heads/test' }
-    let(:results) { Gitlab::ProjectSearchResults.new(project, query, ref) }
+    let(:results) { Gitlab::ProjectSearchResults.new(user, project, query, ref) }
 
     it { expect(results.project).to eq(project) }
     it { expect(results.repository_ref).to eq(ref) }
     it { expect(results.query).to eq('hello world') }
   end
+
+  describe 'confidential issues' do
+    let(:query) { 'issue' }
+    let(:author) { create(:user) }
+    let(:assignee) { create(:user) }
+    let(:non_member) { create(:user) }
+    let(:member) { create(:user) }
+    let(:admin) { create(:admin) }
+    let!(:issue) { create(:issue, project: project, title: 'Issue 1') }
+    let!(:security_issue_1) { create(:issue, :confidential, project: project, title: 'Security issue 1', author: author) }
+    let!(:security_issue_2) { create(:issue, :confidential, title: 'Security issue 2', project: project, assignee: assignee) }
+
+    it 'should not list project confidential issues for non project members' do
+      results = described_class.new(non_member, project, query)
+      issues = results.objects('issues')
+
+      expect(issues).to include issue
+      expect(issues).not_to include security_issue_1
+      expect(issues).not_to include security_issue_2
+      expect(results.issues_count).to eq 1
+    end
+
+    it 'should list project confidential issues for author' do
+      results = described_class.new(author, project, query)
+      issues = results.objects('issues')
+
+      expect(issues).to include issue
+      expect(issues).to include security_issue_1
+      expect(issues).not_to include security_issue_2
+      expect(results.issues_count).to eq 2
+    end
+
+    it 'should list project confidential issues for assignee' do
+      results = described_class.new(assignee, project.id, query)
+      issues = results.objects('issues')
+
+      expect(issues).to include issue
+      expect(issues).not_to include security_issue_1
+      expect(issues).to include security_issue_2
+      expect(results.issues_count).to eq 2
+    end
+
+    it 'should list project confidential issues for project members' do
+      project.team << [member, :developer]
+
+      results = described_class.new(member, project, query)
+      issues = results.objects('issues')
+
+      expect(issues).to include issue
+      expect(issues).to include security_issue_1
+      expect(issues).to include security_issue_2
+      expect(results.issues_count).to eq 3
+    end
+
+    it 'should list all project issues for admin' do
+      results = described_class.new(admin, project, query)
+      issues = results.objects('issues')
+
+      expect(issues).to include issue
+      expect(issues).to include security_issue_1
+      expect(issues).to include security_issue_2
+      expect(results.issues_count).to eq 3
+    end
+  end
 end
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index 7d963795e17b48265d15c55ac92d4b4598716409..7c617723e6d78d7ac20d2e3b0a92446c1e5067c9 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
 
 describe Gitlab::ReferenceExtractor, lib: true do
   let(:project) { create(:project) }
+
   subject { Gitlab::ReferenceExtractor.new(project, project.creator) }
 
   it 'accesses valid user objects' do
@@ -41,6 +42,7 @@ describe Gitlab::ReferenceExtractor, lib: true do
   end
 
   it 'accesses valid issue objects' do
+    project.team << [project.creator, :developer]
     @i0 = create(:issue, project: project)
     @i1 = create(:issue, project: project)
 
@@ -122,4 +124,24 @@ describe Gitlab::ReferenceExtractor, lib: true do
       expect(extracted).to match_array([issue])
     end
   end
+
+  describe '#all' do
+    let(:issue) { create(:issue, project: project) }
+    let(:label) { create(:label, project: project) }
+    let(:text) { "Ref. #{issue.to_reference} and #{label.to_reference}" }
+
+    before do
+      project.team << [project.creator, :developer]
+      subject.analyze(text)
+    end
+
+    it 'returns all referables' do
+      expect(subject.all).to match_array([issue, label])
+    end
+  end
+
+  describe '.references_pattern' do
+    subject { described_class.references_pattern }
+    it { is_expected.to be_kind_of Regexp }
+  end
 end
diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb
index bb18f41785824b1c5e37e9d1ae38f214db8ed333..f4afe597e8d8c415c40b5f3bc48c2e00ac3be71f 100644
--- a/spec/lib/gitlab/search_results_spec.rb
+++ b/spec/lib/gitlab/search_results_spec.rb
@@ -1,6 +1,7 @@
 require 'spec_helper'
 
 describe Gitlab::SearchResults do
+  let(:user) { create(:user) }
   let!(:project) { create(:project, name: 'foo') }
   let!(:issue) { create(:issue, project: project, title: 'foo') }
 
@@ -9,7 +10,7 @@ describe Gitlab::SearchResults do
   end
 
   let!(:milestone) { create(:milestone, project: project, title: 'foo') }
-  let(:results) { described_class.new(Project.all, 'foo') }
+  let(:results) { described_class.new(user, Project.all, 'foo') }
 
   describe '#total_count' do
     it 'returns the total amount of search hits' do
@@ -52,4 +53,92 @@ describe Gitlab::SearchResults do
       expect(results.empty?).to eq(false)
     end
   end
+
+  describe 'confidential issues' do
+    let(:project_1) { create(:empty_project) }
+    let(:project_2) { create(:empty_project) }
+    let(:project_3) { create(:empty_project) }
+    let(:project_4) { create(:empty_project) }
+    let(:query) { 'issue' }
+    let(:limit_projects) { Project.where(id: [project_1.id, project_2.id, project_3.id]) }
+    let(:author) { create(:user) }
+    let(:assignee) { create(:user) }
+    let(:non_member) { create(:user) }
+    let(:member) { create(:user) }
+    let(:admin) { create(:admin) }
+    let!(:issue) { create(:issue, project: project_1, title: 'Issue 1') }
+    let!(:security_issue_1) { create(:issue, :confidential, project: project_1, title: 'Security issue 1', author: author) }
+    let!(:security_issue_2) { create(:issue, :confidential, title: 'Security issue 2', project: project_1, assignee: assignee) }
+    let!(:security_issue_3) { create(:issue, :confidential, project: project_2, title: 'Security issue 3', author: author) }
+    let!(:security_issue_4) { create(:issue, :confidential, project: project_3, title: 'Security issue 4', assignee: assignee) }
+    let!(:security_issue_5) { create(:issue, :confidential, project: project_4, title: 'Security issue 5') }
+
+    it 'should not list confidential issues for non project members' do
+      results = described_class.new(non_member, limit_projects, query)
+      issues = results.objects('issues')
+
+      expect(issues).to include issue
+      expect(issues).not_to include security_issue_1
+      expect(issues).not_to include security_issue_2
+      expect(issues).not_to include security_issue_3
+      expect(issues).not_to include security_issue_4
+      expect(issues).not_to include security_issue_5
+      expect(results.issues_count).to eq 1
+    end
+
+    it 'should list confidential issues for author' do
+      results = described_class.new(author, limit_projects, query)
+      issues = results.objects('issues')
+
+      expect(issues).to include issue
+      expect(issues).to include security_issue_1
+      expect(issues).not_to include security_issue_2
+      expect(issues).to include security_issue_3
+      expect(issues).not_to include security_issue_4
+      expect(issues).not_to include security_issue_5
+      expect(results.issues_count).to eq 3
+    end
+
+    it 'should list confidential issues for assignee' do
+      results = described_class.new(assignee, limit_projects, query)
+      issues = results.objects('issues')
+
+      expect(issues).to include issue
+      expect(issues).not_to include security_issue_1
+      expect(issues).to include security_issue_2
+      expect(issues).not_to include security_issue_3
+      expect(issues).to include security_issue_4
+      expect(issues).not_to include security_issue_5
+      expect(results.issues_count).to eq 3
+    end
+
+    it 'should list confidential issues for project members' do
+      project_1.team << [member, :developer]
+      project_2.team << [member, :developer]
+
+      results = described_class.new(member, limit_projects, query)
+      issues = results.objects('issues')
+
+      expect(issues).to include issue
+      expect(issues).to include security_issue_1
+      expect(issues).to include security_issue_2
+      expect(issues).to include security_issue_3
+      expect(issues).not_to include security_issue_4
+      expect(issues).not_to include security_issue_5
+      expect(results.issues_count).to eq 4
+    end
+
+    it 'should list all issues for admin' do
+      results = described_class.new(admin, limit_projects, query)
+      issues = results.objects('issues')
+
+      expect(issues).to include issue
+      expect(issues).to include security_issue_1
+      expect(issues).to include security_issue_2
+      expect(issues).to include security_issue_3
+      expect(issues).to include security_issue_4
+      expect(issues).not_to include security_issue_5
+      expect(results.issues_count).to eq 5
+    end
+  end
 end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index f910424d85b407f877abc36e2ed3e9bcdc18587c..9b47acfe0cd1027f5023a8fc63a1cfb7496188b2 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -158,6 +158,33 @@ describe Notify do
             is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/
           end
         end
+
+        describe 'moved to another project' do
+          let(:new_issue) { create(:issue) }
+          subject { Notify.issue_moved_email(recipient, issue, new_issue, current_user) }
+
+          it_behaves_like 'an answer to an existing thread', 'issue'
+          it_behaves_like 'it should show Gmail Actions View Issue link'
+          it_behaves_like 'an unsubscribeable thread'
+
+          it 'contains description about action taken' do
+            is_expected.to have_body_text 'Issue was moved to another project'
+          end
+
+          it 'has the correct subject' do
+            is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/i
+          end
+
+          it 'contains link to new issue' do
+            new_issue_url = namespace_project_issue_path(new_issue.project.namespace,
+                                                         new_issue.project, new_issue)
+            is_expected.to have_body_text new_issue_url
+          end
+
+          it 'contains a link to the original issue' do
+            is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/
+          end
+        end
       end
 
       context 'for merge requests' do
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index 253902512c337b178a752f65b5b108050a6c638a..0e9111c8029332a962f3a7cc9d2815c8cdeb569c 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -86,10 +86,21 @@ eos
     let(:issue) { create :issue, project: project }
     let(:other_project) { create :project, :public }
     let(:other_issue) { create :issue, project: other_project }
+    let(:commiter) { create :user }
+
+    before do
+      project.team << [commiter, :developer]
+      other_project.team << [commiter, :developer]
+    end
 
     it 'detects issues that this commit is marked as closing' do
       ext_ref = "#{other_project.path_with_namespace}##{other_issue.iid}"
-      allow(commit).to receive(:safe_message).and_return("Fixes ##{issue.iid} and #{ext_ref}")
+
+      allow(commit).to receive_messages(
+        safe_message: "Fixes ##{issue.iid} and #{ext_ref}",
+        committer_email: commiter.email
+      )
+
       expect(commit.closes_issues).to include(issue)
       expect(commit.closes_issues).to include(other_issue)
     end
diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb
index 20f0c561e44b16e3f9970f387ecc0a8d57e14fd8..cb33edde820d225b73a6fa529ef77aafa714a95c 100644
--- a/spec/models/concerns/mentionable_spec.rb
+++ b/spec/models/concerns/mentionable_spec.rb
@@ -48,7 +48,8 @@ describe Issue, "Mentionable" do
 
   describe '#create_new_cross_references!' do
     let(:project) { create(:project) }
-    let(:issues)  { create_list(:issue, 2, project: project) }
+    let(:author)  { create(:author) }
+    let(:issues)  { create_list(:issue, 2, project: project, author: author) }
 
     context 'before changes are persisted' do
       it 'ignores pre-existing references' do
@@ -91,7 +92,7 @@ describe Issue, "Mentionable" do
     end
 
     def create_issue(description:)
-      create(:issue, project: project, description: description)
+      create(:issue, project: project, description: description, author: author)
     end
   end
 end
diff --git a/spec/models/concerns/milestoneish_spec.rb b/spec/models/concerns/milestoneish_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..47c3be673c56815d3828be81793f5c2fe9f87303
--- /dev/null
+++ b/spec/models/concerns/milestoneish_spec.rb
@@ -0,0 +1,104 @@
+require 'spec_helper'
+
+describe Milestone, 'Milestoneish' do
+  let(:author) { create(:user) }
+  let(:assignee) { create(:user) }
+  let(:non_member) { create(:user) }
+  let(:member) { create(:user) }
+  let(:admin) { create(:admin) }
+  let(:project) { create(:project, :public) }
+  let(:milestone) { create(:milestone, project: project) }
+  let!(:issue) { create(:issue, project: project, milestone: milestone) }
+  let!(:security_issue_1) { create(:issue, :confidential, project: project, author: author, milestone: milestone) }
+  let!(:security_issue_2) { create(:issue, :confidential, project: project, assignee: assignee, milestone: milestone) }
+  let!(:closed_issue_1) { create(:issue, :closed, project: project, milestone: milestone) }
+  let!(:closed_issue_2) { create(:issue, :closed, project: project, milestone: milestone) }
+  let!(:closed_security_issue_1) { create(:issue, :confidential, :closed, project: project, author: author, milestone: milestone) }
+  let!(:closed_security_issue_2) { create(:issue, :confidential, :closed, project: project, assignee: assignee, milestone: milestone) }
+  let!(:closed_security_issue_3) { create(:issue, :confidential, :closed, project: project, author: author, milestone: milestone) }
+  let!(:closed_security_issue_4) { create(:issue, :confidential, :closed, project: project, assignee: assignee, milestone: milestone) }
+  let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, milestone: milestone) }
+
+  before do
+    project.team << [member, :developer]
+  end
+
+  describe '#closed_items_count' do
+    it 'should not count confidential issues for non project members' do
+      expect(milestone.closed_items_count(non_member)).to eq 2
+    end
+
+    it 'should count confidential issues for author' do
+      expect(milestone.closed_items_count(author)).to eq 4
+    end
+
+    it 'should count confidential issues for assignee' do
+      expect(milestone.closed_items_count(assignee)).to eq 4
+    end
+
+    it 'should count confidential issues for project members' do
+      expect(milestone.closed_items_count(member)).to eq 6
+    end
+
+    it 'should count all issues for admin' do
+      expect(milestone.closed_items_count(admin)).to eq 6
+    end
+  end
+
+  describe '#total_items_count' do
+    it 'should not count confidential issues for non project members' do
+      expect(milestone.total_items_count(non_member)).to eq 4
+    end
+
+    it 'should count confidential issues for author' do
+      expect(milestone.total_items_count(author)).to eq 7
+    end
+
+    it 'should count confidential issues for assignee' do
+      expect(milestone.total_items_count(assignee)).to eq 7
+    end
+
+    it 'should count confidential issues for project members' do
+      expect(milestone.total_items_count(member)).to eq 10
+    end
+
+    it 'should count all issues for admin' do
+      expect(milestone.total_items_count(admin)).to eq 10
+    end
+  end
+
+  describe '#complete?' do
+    it 'returns false when has items opened' do
+      expect(milestone.complete?(non_member)).to eq false
+    end
+
+    it 'returns true when all items are closed' do
+      issue.close
+      merge_request.close
+
+      expect(milestone.complete?(non_member)).to eq true
+    end
+  end
+
+  describe '#percent_complete' do
+    it 'should not count confidential issues for non project members' do
+      expect(milestone.percent_complete(non_member)).to eq 50
+    end
+
+    it 'should count confidential issues for author' do
+      expect(milestone.percent_complete(author)).to eq 57
+    end
+
+    it 'should count confidential issues for assignee' do
+      expect(milestone.percent_complete(assignee)).to eq 57
+    end
+
+    it 'should count confidential issues for project members' do
+      expect(milestone.percent_complete(member)).to eq 60
+    end
+
+    it 'should count confidential issues for admin' do
+      expect(milestone.percent_complete(admin)).to eq 60
+    end
+  end
+end
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index ec2a923f91bb4a4c4af968f034afda442e865316..5fe442467385f9f75f0ab24dd669e92ad429ff18 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -65,6 +65,42 @@ describe Event, models: true do
     it { expect(@event.author).to eq(@user) }
   end
 
+  describe '#proper?' do
+    context 'issue event' do
+      let(:project) { create(:empty_project, :public) }
+      let(:non_member) { create(:user) }
+      let(:member)  { create(:user) }
+      let(:author) { create(:author) }
+      let(:assignee) { create(:user) }
+      let(:admin) { create(:admin) }
+      let(:event) { Event.new(project: project, action: Event::CREATED, target: issue, author_id: author.id) }
+
+      before do
+        project.team << [member, :developer]
+      end
+
+      context 'for non confidential issues' do
+        let(:issue) { create(:issue, project: project, author: author, assignee: assignee) }
+
+        it { expect(event.proper?(non_member)).to eq true }
+        it { expect(event.proper?(author)).to eq true }
+        it { expect(event.proper?(assignee)).to eq true }
+        it { expect(event.proper?(member)).to eq true }
+        it { expect(event.proper?(admin)).to eq true }
+      end
+
+      context 'for confidential issues' do
+        let(:issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee) }
+
+        it { expect(event.proper?(non_member)).to eq false }
+        it { expect(event.proper?(author)).to eq true }
+        it { expect(event.proper?(assignee)).to eq true }
+        it { expect(event.proper?(member)).to eq true }
+        it { expect(event.proper?(admin)).to eq true }
+      end
+    end
+  end
+
   describe '.limit_recent' do
     let!(:event1) { create(:closed_issue_event) }
     let!(:event2) { create(:closed_issue_event) }
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index c9245fc953549415b1b973971b8e938cc0046453..7bfca1e72c3c9920b13030dc921ba9dfbc475029 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -56,6 +56,23 @@ describe Group, models: true do
     end
   end
 
+  describe 'scopes' do
+    let!(:private_group)  { create(:group, :private)  }
+    let!(:internal_group) { create(:group, :internal) }
+
+    describe 'public_only' do
+      subject { described_class.public_only.to_a }
+
+      it{ is_expected.to eq([group]) }
+    end
+
+    describe 'public_and_internal_only' do
+      subject { described_class.public_and_internal_only.to_a }
+
+      it{ is_expected.to match_array([group, internal_group]) }
+    end
+  end
+
   describe '#to_reference' do
     it 'returns a String reference to the object' do
       expect(group.to_reference).to eq "@#{group.name}"
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 2ccdec1eeff6008309fb71d91e2bfddf0b32cde6..3c34b1d397f4a3981ba8fcbfd4617a973e1aced5 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -37,6 +37,11 @@ describe Issue, models: true do
 
   subject { create(:issue) }
 
+  describe "act_as_paranoid" do
+    it { is_expected.to have_db_column(:deleted_at) }
+    it { is_expected.to have_db_index(:deleted_at) }
+  end
+
   describe '#to_reference' do
     it 'returns a String reference to the object' do
       expect(subject.to_reference).to eq "##{subject.iid}"
@@ -130,12 +135,62 @@ describe Issue, models: true do
     end
   end
 
+  describe '#can_move?' do
+    let(:user) { create(:user) }
+    let(:issue) { create(:issue) }
+    subject { issue.can_move?(user) }
+
+    context 'user is not a member of project issue belongs to' do
+      it { is_expected.to eq false}
+    end
+
+    context 'user is reporter in project issue belongs to' do
+      let(:project) { create(:project) }
+      let(:issue) { create(:issue, project: project) }
+
+      before { project.team << [user, :reporter] }
+
+      it { is_expected.to eq true }
+
+      context 'checking destination project also' do
+        subject { issue.can_move?(user, to_project) }
+        let(:to_project) { create(:project) }
+
+        context 'destination project allowed' do
+          before { to_project.team << [user, :reporter] }
+          it { is_expected.to eq true }
+        end
+
+        context 'destination project not allowed' do
+          before { to_project.team << [user, :guest] }
+          it { is_expected.to eq false }
+        end
+      end
+    end
+  end
+
+  describe '#moved?' do
+    let(:issue) { create(:issue) }
+    subject { issue.moved? }
+
+    context 'issue not moved' do
+      it { is_expected.to eq false }
+    end
+
+    context 'issue already moved' do
+      let(:moved_to_issue) { create(:issue) }
+      let(:issue) { create(:issue, moved_to: moved_to_issue) }
+
+      it { is_expected.to eq true }
+    end
+  end
+
   describe '#related_branches' do
-    it "should " do
+    it "selects the right branches" do
       allow(subject.project.repository).to receive(:branch_names).
-                                    and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name])
+        and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name])
 
-      expect(subject.related_branches).to eq [subject.to_branch_name]
+      expect(subject.related_branches).to eq([subject.to_branch_name])
     end
   end
 
@@ -151,10 +206,10 @@ describe Issue, models: true do
   end
 
   describe "#to_branch_name" do
-    let(:issue) { build(:issue, title: 'a' * 30) }
+    let(:issue) { create(:issue, title: 'a' * 30) }
 
     it "starts with the issue iid" do
-      expect(issue.to_branch_name).to match /\A#{issue.iid}-a+\z/
+      expect(issue.to_branch_name).to match /-#{issue.iid}\z/
     end
   end
 end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 8bf68013fd2668519203cf29839db612cec822dc..bd0a4ebe3371faccc88bd8b19e03cc98882eb67c 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -49,6 +49,11 @@ describe MergeRequest, models: true do
     it { is_expected.to include_module(Taskable) }
   end
 
+  describe "act_as_paranoid" do
+    it { is_expected.to have_db_column(:deleted_at) }
+    it { is_expected.to have_db_index(:deleted_at) }
+  end
+
   describe 'validation' do
     it { is_expected.to validate_presence_of(:target_branch) }
     it { is_expected.to validate_presence_of(:source_branch) }
@@ -86,6 +91,41 @@ describe MergeRequest, models: true do
     end
   end
 
+  describe '#target_sha' do
+    context 'when the target branch does not exist anymore' do
+      subject { create(:merge_request).tap { |mr| mr.update_attribute(:target_branch, 'deleted') } }
+
+      it 'returns nil' do
+        expect(subject.target_sha).to be_nil
+      end
+    end
+  end
+
+  describe '#source_sha' do
+    let(:last_branch_commit) { subject.source_project.repository.commit(subject.source_branch) }
+
+    context 'with diffs' do
+      subject { create(:merge_request, :with_diffs) }
+      it 'returns the sha of the source branch last commit' do
+        expect(subject.source_sha).to eq(last_branch_commit.sha)
+      end
+    end
+
+    context 'without diffs' do
+      subject { create(:merge_request, :without_diffs) }
+      it 'returns the sha of the source branch last commit' do
+        expect(subject.source_sha).to eq(last_branch_commit.sha)
+      end
+    end
+
+    context 'when the merge request is being created' do
+      subject { build(:merge_request, source_branch: nil, compare_commits: []) }
+      it 'returns nil' do
+        expect(subject.source_sha).to be_nil
+      end
+    end
+  end
+
   describe '#to_reference' do
     it 'returns a String reference to the object' do
       expect(subject.to_reference).to eq "!#{subject.iid}"
@@ -150,6 +190,7 @@ describe MergeRequest, models: true do
     let(:commit2) { double('commit2', safe_message: "Fixes #{issue1.to_reference}") }
 
     before do
+      subject.project.team << [subject.author, :developer]
       allow(subject).to receive(:commits).and_return([commit0, commit1, commit2])
     end
 
@@ -180,24 +221,11 @@ describe MergeRequest, models: true do
   end
 
   describe "#work_in_progress?" do
-    it "detects the 'WIP ' prefix" do
-      subject.title = "WIP #{subject.title}"
-      expect(subject).to be_work_in_progress
-    end
-
-    it "detects the 'WIP: ' prefix" do
-      subject.title = "WIP: #{subject.title}"
-      expect(subject).to be_work_in_progress
-    end
-
-    it "detects the '[WIP] ' prefix" do
-      subject.title = "[WIP] #{subject.title}"
-      expect(subject).to be_work_in_progress
-    end
-
-    it "detects the '[WIP]' prefix" do
-      subject.title = "[WIP]#{subject.title}"
-      expect(subject).to be_work_in_progress
+    ['WIP ', 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', ' [WIP] WIP [WIP] WIP: WIP '].each do |wip_prefix|
+      it "detects the '#{wip_prefix}' prefix" do
+        subject.title = "#{wip_prefix}#{subject.title}"
+        expect(subject).to be_work_in_progress
+      end
     end
 
     it "doesn't detect WIP for words starting with WIP" do
@@ -205,6 +233,11 @@ describe MergeRequest, models: true do
       expect(subject).not_to be_work_in_progress
     end
 
+    it "doesn't detect WIP for words containing with WIP" do
+      subject.title = "WupWipwap #{subject.title}"
+      expect(subject).not_to be_work_in_progress
+    end
+
     it "doesn't detect WIP by default" do
       expect(subject).not_to be_work_in_progress
     end
@@ -284,6 +317,18 @@ describe MergeRequest, models: true do
     let(:project)      { create(:project) }
     let(:fork_project) { create(:project, forked_from_project: project) }
 
+    context 'when the target branch does not exist anymore' do
+      subject { create(:merge_request).tap { |mr| mr.update_attribute(:target_branch, 'deleted') } }
+
+      it 'does not crash' do
+        expect{ subject.diverged_commits_count }.not_to raise_error
+      end
+
+      it 'returns 0' do
+        expect(subject.diverged_commits_count).to eq(0)
+      end
+    end
+
     context 'diverged on same repository' do
       subject(:merge_request_with_divergence) { create(:merge_request, :diverged, source_project: project, target_project: project) }
 
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index de1757bf67a5f36f98bd9c51553fa0d170978494..72a4ea702281ae5d8dd3b8fbe2c6e86768e1821b 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -32,6 +32,7 @@ describe Milestone, models: true do
 
   let(:milestone) { create(:milestone) }
   let(:issue) { create(:issue) }
+  let(:user) { create(:user) }
 
   describe "unique milestone title per project" do
     it "shouldn't accept the same title in a project twice" do
@@ -50,18 +51,17 @@ describe Milestone, models: true do
   describe "#percent_complete" do
     it "should not count open issues" do
       milestone.issues << issue
-      expect(milestone.percent_complete).to eq(0)
+      expect(milestone.percent_complete(user)).to eq(0)
     end
 
     it "should count closed issues" do
       issue.close
       milestone.issues << issue
-      expect(milestone.percent_complete).to eq(100)
+      expect(milestone.percent_complete(user)).to eq(100)
     end
 
     it "should recover from dividing by zero" do
-      expect(milestone.issues).to receive(:size).and_return(0)
-      expect(milestone.percent_complete).to eq(0)
+      expect(milestone.percent_complete(user)).to eq(0)
     end
   end
 
@@ -103,7 +103,7 @@ describe Milestone, models: true do
       )
     end
 
-    it { expect(milestone.percent_complete).to eq(75) }
+    it { expect(milestone.percent_complete(user)).to eq(75) }
   end
 
   describe :items_count do
@@ -113,23 +113,23 @@ describe Milestone, models: true do
       milestone.merge_requests << create(:merge_request)
     end
 
-    it { expect(milestone.closed_items_count).to eq(1) }
-    it { expect(milestone.total_items_count).to eq(3) }
-    it { expect(milestone.is_empty?).to be_falsey }
+    it { expect(milestone.closed_items_count(user)).to eq(1) }
+    it { expect(milestone.total_items_count(user)).to eq(3) }
+    it { expect(milestone.is_empty?(user)).to be_falsey }
   end
 
   describe :can_be_closed? do
     it { expect(milestone.can_be_closed?).to be_truthy }
   end
 
-  describe :is_empty? do
+  describe :total_items_count do
     before do
       create :closed_issue, milestone: milestone
       create :merge_request, milestone: milestone
     end
 
     it 'Should return total count of issues and merge requests assigned to milestone' do
-      expect(milestone.total_items_count).to eq 2
+      expect(milestone.total_items_count(user)).to eq 2
     end
   end
 
diff --git a/spec/models/project_security_spec.rb b/spec/models/project_security_spec.rb
index 3643ad1b0523e7b5d4d63b77a0922ec4551ae9a5..e12258c0874200f7ca0cf71d183d3ac7cc4ed711 100644
--- a/spec/models/project_security_spec.rb
+++ b/spec/models/project_security_spec.rb
@@ -18,11 +18,11 @@ describe Project, models: true do
     let(:report_actions) { Ability.project_report_rules }
     let(:dev_actions) { Ability.project_dev_rules }
     let(:master_actions) { Ability.project_master_rules }
-    let(:admin_actions) { Ability.project_admin_rules }
+    let(:owner_actions) { Ability.project_owner_rules }
 
     describe "Non member rules" do
       it "should deny for non-project users any actions" do
-        admin_actions.each do |action|
+        owner_actions.each do |action|
           expect(@abilities.allowed?(@u1, action, @p1)).to be_falsey
         end
       end
@@ -90,20 +90,20 @@ describe Project, models: true do
       end
     end
 
-    describe "Admin Rules" do
+    describe "Owner Rules" do
       before do
         @p1.project_members.create(project: @p1, user: @u2, access_level: ProjectMember::DEVELOPER)
         @p1.project_members.create(project: @p1, user: @u3, access_level: ProjectMember::MASTER)
       end
 
       it "should deny for masters admin-specific actions" do
-        [admin_actions - master_actions].each do |action|
+        [owner_actions - master_actions].each do |action|
           expect(@abilities.allowed?(@u2, action, @p1)).to be_falsey
         end
       end
 
       it "should allow for project owner any admin actions" do
-        admin_actions.each do |action|
+        owner_actions.each do |action|
           expect(@abilities.allowed?(@u4, action, @p1)).to be_truthy
         end
       end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index b8b9a455b836a547fc57d9fb2afd9bcf864054a4..20f06f4b7e1412cc8d643ca23b566e249dc621d2 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -442,7 +442,7 @@ describe Project, models: true do
   end
 
   describe '.trending' do
-    let(:group)    { create(:group) }
+    let(:group)    { create(:group, :public) }
     let(:project1) { create(:empty_project, :public, group: group) }
     let(:project2) { create(:empty_project, :public, group: group) }
 
@@ -571,12 +571,8 @@ describe Project, models: true do
     end
 
     context 'when checking on forked project' do
-      let(:forked_project) { create :forked_project_with_submodules }
-
-      before do
-        forked_project.build_forked_project_link(forked_to_project_id: forked_project.id, forked_from_project_id: project.id)
-        forked_project.save
-      end
+      let(:project)        { create(:project, :internal) }
+      let(:forked_project) { create(:project, forked_from_project: project) }
 
       it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy }
       it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_truthy }
@@ -720,4 +716,61 @@ describe Project, models: true do
       expect(described_class.search_by_title('KITTENS')).to eq([project])
     end
   end
+
+  context 'when checking projects from groups' do
+    let(:private_group)    { create(:group, visibility_level: 0)  }
+    let(:internal_group)   { create(:group, visibility_level: 10) }
+
+    let(:private_project)  { create :project, :private, group: private_group }
+    let(:internal_project) { create :project, :internal, group: internal_group }
+
+    context 'when group is private project can not be internal' do
+      it { expect(private_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_falsey }
+    end
+
+    context 'when group is internal project can not be public' do
+      it { expect(internal_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_falsey }
+    end
+  end
+
+  describe '#create_repository' do
+    let(:project) { create(:project) }
+    let(:shell) { Gitlab::Shell.new }
+
+    before do
+      allow(project).to receive(:gitlab_shell).and_return(shell)
+    end
+
+    context 'using a regular repository' do
+      it 'creates the repository' do
+        expect(shell).to receive(:add_repository).
+          with(project.path_with_namespace).
+          and_return(true)
+
+        expect(project.repository).to receive(:after_create)
+
+        expect(project.create_repository).to eq(true)
+      end
+
+      it 'adds an error if the repository could not be created' do
+        expect(shell).to receive(:add_repository).
+          with(project.path_with_namespace).
+          and_return(false)
+
+        expect(project.repository).not_to receive(:after_create)
+
+        expect(project.create_repository).to eq(false)
+        expect(project.errors).not_to be_empty
+      end
+    end
+
+    context 'using a forked repository' do
+      it 'does nothing' do
+        expect(project).to receive(:forked?).and_return(true)
+        expect(shell).not_to receive(:add_repository)
+
+        project.create_repository
+      end
+    end
+  end
 end
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index a2085df5bcd6aa79c588fc2cc63a7bf558809e4f..532e3f013fd0aa692d77e84ba88c37d753c757f6 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -244,6 +244,18 @@ describe ProjectWiki, models: true do
     end
   end
 
+  describe '#create_repo!' do
+    it 'creates a repository' do
+      expect(subject).to receive(:init_repo).
+        with(subject.path_with_namespace).
+        and_return(true)
+
+      expect(subject.repository).to receive(:after_create)
+
+      expect(subject.create_repo!).to be_an_instance_of(Gollum::Wiki)
+    end
+  end
+
   private
 
   def create_temp_repo(path)
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index fc2ab2d993172b4a4aa73a4ca3a63beb38cc5621..7eac70ae9480a6fd791b07d1a5aaae7f75253188 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -537,6 +537,12 @@ describe Repository, models: true do
 
         repository.before_delete
       end
+
+      it 'flushes the exists cache' do
+        expect(repository).to receive(:expire_exists_cache)
+
+        repository.before_delete
+      end
     end
 
     describe 'when a repository exists' do
@@ -593,13 +599,19 @@ describe Repository, models: true do
 
       repository.after_import
     end
+
+    it 'flushes the exists cache' do
+      expect(repository).to receive(:expire_exists_cache)
+
+      repository.after_import
+    end
   end
 
   describe '#after_push_commit' do
     it 'flushes the cache' do
-      expect(repository).to receive(:expire_cache).with('master')
+      expect(repository).to receive(:expire_cache).with('master', '123')
 
-      repository.after_push_commit('master')
+      repository.after_push_commit('master', '123')
     end
   end
 
@@ -619,6 +631,14 @@ describe Repository, models: true do
     end
   end
 
+  describe '#after_create' do
+    it 'flushes the exists cache' do
+      expect(repository).to receive(:expire_exists_cache)
+
+      repository.after_create
+    end
+  end
+
   describe "#main_language" do
     it 'shows the main language of the project' do
       expect(repository.main_language).to eq("Ruby")
@@ -703,4 +723,121 @@ describe Repository, models: true do
       repository.rm_tag('8.5')
     end
   end
+
+  describe '#avatar' do
+    it 'returns the first avatar file found in the repository' do
+      expect(repository).to receive(:blob_at_branch).
+        with('master', 'logo.png').
+        and_return(true)
+
+      expect(repository.avatar).to eq('logo.png')
+    end
+
+    it 'caches the output' do
+      allow(repository).to receive(:blob_at_branch).
+        with('master', 'logo.png').
+        and_return(true)
+
+      expect(repository.avatar).to eq('logo.png')
+
+      expect(repository).to_not receive(:blob_at_branch)
+      expect(repository.avatar).to eq('logo.png')
+    end
+  end
+
+  describe '#expire_avatar_cache' do
+    let(:cache) { repository.send(:cache) }
+
+    before do
+      allow(repository).to receive(:cache).and_return(cache)
+    end
+
+    context 'without a branch or revision' do
+      it 'flushes the cache' do
+        expect(cache).to receive(:expire).with(:avatar)
+
+        repository.expire_avatar_cache
+      end
+    end
+
+    context 'with a branch' do
+      it 'does not flush the cache if the branch is not the default branch' do
+        expect(cache).not_to receive(:expire)
+
+        repository.expire_avatar_cache('cats')
+      end
+
+      it 'flushes the cache if the branch equals the default branch' do
+        expect(cache).to receive(:expire).with(:avatar)
+
+        repository.expire_avatar_cache(repository.root_ref)
+      end
+    end
+
+    context 'with a branch and revision' do
+      let(:commit) { double(:commit) }
+
+      before do
+        allow(repository).to receive(:commit).and_return(commit)
+      end
+
+      it 'does not flush the cache if the commit does not change any logos' do
+        diff = double(:diff, new_path: 'test.txt')
+
+        expect(commit).to receive(:diffs).and_return([diff])
+        expect(cache).not_to receive(:expire)
+
+        repository.expire_avatar_cache(repository.root_ref, '123')
+      end
+
+      it 'flushes the cache if the commit changes any of the logos' do
+        diff = double(:diff, new_path: Repository::AVATAR_FILES[0])
+
+        expect(commit).to receive(:diffs).and_return([diff])
+        expect(cache).to receive(:expire).with(:avatar)
+
+        repository.expire_avatar_cache(repository.root_ref, '123')
+      end
+    end
+  end
+
+  describe '#expire_exists_cache' do
+    let(:cache) { repository.send(:cache) }
+
+    it 'expires the cache' do
+      expect(cache).to receive(:expire).with(:exists?)
+
+      repository.expire_exists_cache
+    end
+  end
+
+  describe '#build_cache' do
+    let(:cache) { repository.send(:cache) }
+
+    it 'builds the caches if they do not already exist' do
+      expect(cache).to receive(:exist?).
+        exactly(repository.cache_keys.length).
+        times.
+        and_return(false)
+
+      repository.cache_keys.each do |key|
+        expect(repository).to receive(key)
+      end
+
+      repository.build_cache
+    end
+
+    it 'does not build any caches that already exist' do
+      expect(cache).to receive(:exist?).
+        exactly(repository.cache_keys.length).
+        times.
+        and_return(true)
+
+      repository.cache_keys.each do |key|
+        expect(repository).to_not receive(key)
+      end
+
+      repository.build_cache
+    end
+  end
 end
diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb
index fe9ea7e7d1ec4b2d9ae4366a2eb0acab8291329f..d9b86b9368fd0f704180edf5b346790e7fa23fda 100644
--- a/spec/models/todo_spec.rb
+++ b/spec/models/todo_spec.rb
@@ -5,19 +5,24 @@
 #  id          :integer          not null, primary key
 #  user_id     :integer          not null
 #  project_id  :integer          not null
-#  target_id   :integer          not null
+#  target_id   :integer
 #  target_type :string           not null
 #  author_id   :integer
-#  note_id     :integer
 #  action      :integer          not null
 #  state       :string           not null
 #  created_at  :datetime
 #  updated_at  :datetime
+#  note_id     :integer
+#  commit_id   :string
 #
 
 require 'spec_helper'
 
 describe Todo, models: true do
+  let(:project) { create(:project) }
+  let(:commit) { project.commit }
+  let(:issue) { create(:issue) }
+
   describe 'relationships' do
     it { is_expected.to belong_to(:author).class_name("User") }
     it { is_expected.to belong_to(:note) }
@@ -33,8 +38,22 @@ describe Todo, models: true do
 
   describe 'validations' do
     it { is_expected.to validate_presence_of(:action) }
-    it { is_expected.to validate_presence_of(:target) }
+    it { is_expected.to validate_presence_of(:target_type) }
     it { is_expected.to validate_presence_of(:user) }
+
+    context 'for commits' do
+      subject { described_class.new(target_type: 'Commit') }
+
+      it { is_expected.to validate_presence_of(:commit_id) }
+      it { is_expected.not_to validate_presence_of(:target_id) }
+    end
+
+    context 'for issuables' do
+      subject { described_class.new(target: issue) }
+
+      it { is_expected.to validate_presence_of(:target_id) }
+      it { is_expected.not_to validate_presence_of(:commit_id) }
+    end
   end
 
   describe '#body' do
@@ -55,15 +74,69 @@ describe Todo, models: true do
     end
   end
 
-  describe '#done!' do
+  describe '#done' do
     it 'changes state to done' do
       todo = create(:todo, state: :pending)
-      expect { todo.done! }.to change(todo, :state).from('pending').to('done')
+      expect { todo.done }.to change(todo, :state).from('pending').to('done')
     end
 
     it 'does not raise error when is already done' do
       todo = create(:todo, state: :done)
-      expect { todo.done! }.not_to raise_error
+      expect { todo.done }.not_to raise_error
+    end
+  end
+
+  describe '#for_commit?' do
+    it 'returns true when target is a commit' do
+      subject.target_type = 'Commit'
+      expect(subject.for_commit?).to eq true
+    end
+
+    it 'returns false when target is an issuable' do
+      subject.target_type = 'Issue'
+      expect(subject.for_commit?).to eq false
+    end
+  end
+
+  describe '#target' do
+    context 'for commits' do
+      it 'returns an instance of Commit when exists' do
+        subject.project = project
+        subject.target_type = 'Commit'
+        subject.commit_id = commit.id
+
+        expect(subject.target).to be_a(Commit)
+        expect(subject.target).to eq commit
+      end
+
+      it 'returns nil when does not exists' do
+        subject.project = project
+        subject.target_type = 'Commit'
+        subject.commit_id = 'xxxx'
+
+        expect(subject.target).to be_nil
+      end
+    end
+
+    it 'returns the issuable for issuables' do
+      subject.target_id = issue.id
+      subject.target_type = issue.class.name
+      expect(subject.target).to eq issue
+    end
+  end
+
+  describe '#target_reference' do
+    it 'returns the short commit id for commits' do
+      subject.project = project
+      subject.target_type = 'Commit'
+      subject.commit_id = commit.id
+
+      expect(subject.target_reference).to eq commit.short_id
+    end
+
+    it 'returns reference for issuables' do
+      subject.target = issue
+      expect(subject.target_reference).to eq issue.to_reference
     end
   end
 end
diff --git a/spec/requests/api/group_members_spec.rb b/spec/requests/api/group_members_spec.rb
index dd5baa44cb2ea242936b85869d7d30b613ae3bdc..3e8b4aa1f88d455f9d9b9ce164803d4720ea1a38 100644
--- a/spec/requests/api/group_members_spec.rb
+++ b/spec/requests/api/group_members_spec.rb
@@ -11,7 +11,7 @@ describe API::API, api: true  do
   let(:stranger) { create(:user) }
 
   let!(:group_with_members) do
-    group = create(:group)
+    group = create(:group, :private)
     group.add_users([reporter.id], GroupMember::REPORTER)
     group.add_users([developer.id], GroupMember::DEVELOPER)
     group.add_users([master.id], GroupMember::MASTER)
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 4cfa49d15666d06deeee90d6ba0181acde3978c5..41c9cacd455d051044cde40db64db8e16ebdb793 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -9,7 +9,7 @@ describe API::API, api: true  do
   let(:admin) { create(:admin) }
   let(:avatar_file_path) { File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') }
   let!(:group1) { create(:group, avatar: File.open(avatar_file_path)) }
-  let!(:group2) { create(:group) }
+  let!(:group2) { create(:group, :private) }
   let!(:project1) { create(:project, namespace: group1) }
   let!(:project2) { create(:project, namespace: group2) }
 
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 571ea2dae4c75263c4bb3c58b596bbe5d5513937..ce55cb7b0ae2f95bc2ad32ab05fdc98779e8919b 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -2,8 +2,12 @@ require 'spec_helper'
 
 describe API::API, api: true  do
   include ApiHelpers
-  let(:user) { create(:user) }
-  let!(:project) { create(:project, namespace: user.namespace ) }
+  let(:user)        { create(:user) }
+  let(:non_member)  { create(:user) }
+  let(:author)      { create(:author) }
+  let(:assignee)    { create(:assignee) }
+  let(:admin)       { create(:user, :admin) }
+  let!(:project)    { create(:project, :public, namespace: user.namespace ) }
   let!(:closed_issue) do
     create :closed_issue,
            author: user,
@@ -12,6 +16,13 @@ describe API::API, api: true  do
            state: :closed,
            milestone: milestone
   end
+  let!(:confidential_issue) do
+    create :issue,
+           :confidential,
+           project: project,
+           author: author,
+           assignee: assignee
+  end
   let!(:issue) do
     create :issue,
            author: user,
@@ -123,10 +134,43 @@ describe API::API, api: true  do
     let(:base_url) { "/projects/#{project.id}" }
     let(:title) { milestone.title }
 
-    it "should return project issues" do
+    it 'should return project issues without confidential issues for non project members' do
+      get api("#{base_url}/issues", non_member)
+      expect(response.status).to eq(200)
+      expect(json_response).to be_an Array
+      expect(json_response.length).to eq(2)
+      expect(json_response.first['title']).to eq(issue.title)
+    end
+
+    it 'should return project confidential issues for author' do
+      get api("#{base_url}/issues", author)
+      expect(response.status).to eq(200)
+      expect(json_response).to be_an Array
+      expect(json_response.length).to eq(3)
+      expect(json_response.first['title']).to eq(issue.title)
+    end
+
+    it 'should return project confidential issues for assignee' do
+      get api("#{base_url}/issues", assignee)
+      expect(response.status).to eq(200)
+      expect(json_response).to be_an Array
+      expect(json_response.length).to eq(3)
+      expect(json_response.first['title']).to eq(issue.title)
+    end
+
+    it 'should return project issues with confidential issues for project members' do
       get api("#{base_url}/issues", user)
       expect(response.status).to eq(200)
       expect(json_response).to be_an Array
+      expect(json_response.length).to eq(3)
+      expect(json_response.first['title']).to eq(issue.title)
+    end
+
+    it 'should return project confidential issues for admin' do
+      get api("#{base_url}/issues", admin)
+      expect(response.status).to eq(200)
+      expect(json_response).to be_an Array
+      expect(json_response.length).to eq(3)
       expect(json_response.first['title']).to eq(issue.title)
     end
 
@@ -206,6 +250,41 @@ describe API::API, api: true  do
       get api("/projects/#{project.id}/issues/54321", user)
       expect(response.status).to eq(404)
     end
+
+    context 'confidential issues' do
+      it "should return 404 for non project members" do
+        get api("/projects/#{project.id}/issues/#{confidential_issue.id}", non_member)
+        expect(response.status).to eq(404)
+      end
+
+      it "should return confidential issue for project members" do
+        get api("/projects/#{project.id}/issues/#{confidential_issue.id}", user)
+        expect(response.status).to eq(200)
+        expect(json_response['title']).to eq(confidential_issue.title)
+        expect(json_response['iid']).to eq(confidential_issue.iid)
+      end
+
+      it "should return confidential issue for author" do
+        get api("/projects/#{project.id}/issues/#{confidential_issue.id}", author)
+        expect(response.status).to eq(200)
+        expect(json_response['title']).to eq(confidential_issue.title)
+        expect(json_response['iid']).to eq(confidential_issue.iid)
+      end
+
+      it "should return confidential issue for assignee" do
+        get api("/projects/#{project.id}/issues/#{confidential_issue.id}", assignee)
+        expect(response.status).to eq(200)
+        expect(json_response['title']).to eq(confidential_issue.title)
+        expect(json_response['iid']).to eq(confidential_issue.iid)
+      end
+
+      it "should return confidential issue for admin" do
+        get api("/projects/#{project.id}/issues/#{confidential_issue.id}", admin)
+        expect(response.status).to eq(200)
+        expect(json_response['title']).to eq(confidential_issue.title)
+        expect(json_response['iid']).to eq(confidential_issue.iid)
+      end
+    end
   end
 
   describe "POST /projects/:id/issues" do
@@ -294,6 +373,35 @@ describe API::API, api: true  do
       expect(response.status).to eq(400)
       expect(json_response['message']['labels']['?']['title']).to eq(['is invalid'])
     end
+
+    context 'confidential issues' do
+      it "should return 403 for non project members" do
+        put api("/projects/#{project.id}/issues/#{confidential_issue.id}", non_member),
+          title: 'updated title'
+        expect(response.status).to eq(403)
+      end
+
+      it "should update a confidential issue for project members" do
+        put api("/projects/#{project.id}/issues/#{confidential_issue.id}", user),
+          title: 'updated title'
+        expect(response.status).to eq(200)
+        expect(json_response['title']).to eq('updated title')
+      end
+
+      it "should update a confidential issue for author" do
+        put api("/projects/#{project.id}/issues/#{confidential_issue.id}", author),
+          title: 'updated title'
+        expect(response.status).to eq(200)
+        expect(json_response['title']).to eq('updated title')
+      end
+
+      it "should update a confidential issue for admin" do
+        put api("/projects/#{project.id}/issues/#{confidential_issue.id}", admin),
+          title: 'updated title'
+        expect(response.status).to eq(200)
+        expect(json_response['title']).to eq('updated title')
+      end
+    end
   end
 
   describe 'PUT /projects/:id/issues/:issue_id to update labels' do
@@ -361,9 +469,25 @@ describe API::API, api: true  do
   end
 
   describe "DELETE /projects/:id/issues/:issue_id" do
-    it "should delete a project issue" do
-      delete api("/projects/#{project.id}/issues/#{issue.id}", user)
-      expect(response.status).to eq(405)
+    it "rejects a non member from deleting an issue" do
+      delete api("/projects/#{project.id}/issues/#{issue.id}", non_member)
+      expect(response.status).to be(403)
+    end
+
+    it "rejects a developer from deleting an issue" do
+      delete api("/projects/#{project.id}/issues/#{issue.id}", author)
+      expect(response.status).to be(403)
+    end
+
+    context "when the user is project owner" do
+      let(:owner)     { create(:user) }
+      let(:project)   { create(:project, namespace: owner.namespace) }
+
+      it "deletes the issue if an admin requests it" do
+        delete api("/projects/#{project.id}/issues/#{issue.id}", owner)
+        expect(response.status).to eq(200)
+        expect(json_response['state']).to eq 'opened'
+      end
     end
   end
 end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 4fd1df25568a90396fae7fb503a9cae8b0f4acd0..c9175a4d6eba57ff84bf0db573c59c7e992fa8b0 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -2,15 +2,17 @@ require "spec_helper"
 
 describe API::API, api: true  do
   include ApiHelpers
-  let(:base_time) { Time.now }
-  let(:user) { create(:user) }
-  let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) }
+  let(:base_time)   { Time.now }
+  let(:user)        { create(:user) }
+  let(:admin)       { create(:user, :admin) }
+  let(:non_member)  { create(:user) }
+  let!(:project)    { create(:project, creator_id: user.id, namespace: user.namespace) }
   let!(:merge_request) { create(:merge_request, :simple, author: user, assignee: user, source_project: project, target_project: project, title: "Test", created_at: base_time) }
   let!(:merge_request_closed) { create(:merge_request, state: "closed", author: user, assignee: user, source_project: project, target_project: project, title: "Closed test", created_at: base_time + 1.second) }
   let!(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignee: user, source_project: project, target_project: project, title: "Merged test", created_at: base_time + 2.seconds) }
-  let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
-  let!(:note2) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") }
-  let(:milestone) { create(:milestone, title: '1.0.0', project: project) }
+  let!(:note)       { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
+  let!(:note2)      { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") }
+  let(:milestone)   { create(:milestone, title: '1.0.0', project: project) }
 
   before do
     project.team << [user, :reporters]
@@ -315,6 +317,29 @@ describe API::API, api: true  do
     end
   end
 
+  describe "DELETE /projects/:id/merge_requests/:merge_request_id" do
+    context "when the user is developer" do
+      let(:developer) { create(:user) }
+
+      before do
+        project.team << [developer, :developer]
+      end
+
+      it "denies the deletion of the merge request" do
+        delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}", developer)
+        expect(response.status).to be(403)
+      end
+    end
+
+    context "when the user is project owner" do
+      it "destroys the merge request owners can destroy" do
+        delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user)
+
+        expect(response.status).to eq(200)
+      end
+    end
+  end
+
   describe "PUT /projects/:id/merge_requests/:merge_request_id to close MR" do
     it "should return merge_request" do
       put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), state_event: "close"
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index a6699cdc81cf3c60332f30ff6e3c7486f31a9f04..a5d4985dc78e76a332b73aea1bad639bd6c31d26 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -275,6 +275,7 @@ describe API::API, api: true  do
 
       it 'should not allow a non-admin to use a restricted visibility level' do
         post api('/projects', user), @project
+
         expect(response.status).to eq(400)
         expect(json_response['message']['visibility_level'].first).to(
           match('restricted by your GitLab administrator')
diff --git a/spec/services/create_snippet_service_spec.rb b/spec/services/create_snippet_service_spec.rb
index c800dea04fa14786e748711070d6a86cb6c9903a..7a850066bf86b8e275cc360710f63e87e59190fb 100644
--- a/spec/services/create_snippet_service_spec.rb
+++ b/spec/services/create_snippet_service_spec.rb
@@ -23,7 +23,7 @@ describe CreateSnippetService, services: true do
       snippet = create_snippet(nil, @user, @opts)
       expect(snippet.errors.messages).to have_key(:visibility_level)
       expect(snippet.errors.messages[:visibility_level].first).to(
-        match('Public visibility has been restricted')
+        match('has been restricted')
       )
     end
 
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index 145bc9375605763eb0c62dccc9a4fb2f43f9e8c5..8490a729e517f766e0d56f005c4f5ded72aeb13c 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -29,7 +29,8 @@ describe GitPushService, services: true do
       it { is_expected.to be_truthy }
 
       it 'flushes general cached data' do
-        expect(project.repository).to receive(:expire_cache).with('master')
+        expect(project.repository).to receive(:expire_cache).
+          with('master', newrev)
 
         subject
       end
@@ -46,7 +47,8 @@ describe GitPushService, services: true do
       it { is_expected.to be_truthy }
 
       it 'flushes general cached data' do
-        expect(project.repository).to receive(:expire_cache).with('master')
+        expect(project.repository).to receive(:expire_cache).
+          with('master', newrev)
 
         subject
       end
@@ -65,7 +67,8 @@ describe GitPushService, services: true do
       end
 
       it 'flushes general cached data' do
-        expect(project.repository).to receive(:expire_cache).with('master')
+        expect(project.repository).to receive(:expire_cache).
+          with('master', newrev)
 
         subject
       end
@@ -212,12 +215,16 @@ describe GitPushService, services: true do
     let(:commit) { project.commit }
 
     before do
+      project.team << [commit_author, :developer]
+      project.team << [user, :developer]
+
       allow(commit).to receive_messages(
         safe_message: "this commit \n mentions #{issue.to_reference}",
         references: [issue],
         author_name: commit_author.name,
         author_email: commit_author.email
       )
+
       allow(project.repository).to receive(:commits_between).and_return([commit])
     end
 
diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6aefb48a4e8ff82b410d3aa54ce9b8d0b4e7fab7
--- /dev/null
+++ b/spec/services/groups/create_service_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe Groups::CreateService, services: true do
+  let!(:user)         { create(:user) }
+  let!(:group_params) { { path: "group_path", visibility_level: Gitlab::VisibilityLevel::PUBLIC } }
+
+  describe "execute" do
+    let!(:service) { described_class.new(user, group_params ) }
+    subject { service.execute }
+
+    context "create groups without restricted visibility level" do
+      it { is_expected.to be_persisted }
+    end
+
+    context "cannot create group with restricted visibility level" do
+      before { allow(current_application_settings).to receive(:restricted_visibility_levels).and_return([Gitlab::VisibilityLevel::PUBLIC]) }
+      it { is_expected.to_not be_persisted }
+    end
+  end
+end
diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9c2331144a04d09d674d6b7f6c19ec7d53f4fa75
--- /dev/null
+++ b/spec/services/groups/update_service_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+describe Groups::UpdateService, services: true do
+  let!(:user)             { create(:user) }
+  let!(:private_group)    { create(:group, :private) }
+  let!(:internal_group)   { create(:group, :internal) }
+  let!(:public_group)     { create(:group, :public) }
+
+  describe "#execute" do
+    context "project visibility_level validation" do
+      context "public group with public projects" do
+        let!(:service) { described_class.new(public_group, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL ) }
+
+        before do
+          public_group.add_user(user, Gitlab::Access::MASTER)
+          create(:project, :public, group: public_group)
+        end
+
+        it "does not change permission level" do
+          service.execute
+          expect(public_group.errors.count).to eq(1)
+        end
+      end
+
+      context "internal group with internal project" do
+        let!(:service) { described_class.new(internal_group, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE ) }
+
+        before do
+          internal_group.add_user(user, Gitlab::Access::MASTER)
+          create(:project, :internal, group: internal_group)
+        end
+
+        it "does not change permission level" do
+          service.execute
+          expect(internal_group.errors.count).to eq(1)
+        end
+      end
+    end
+  end
+
+  context "unauthorized visibility_level validation" do
+    let!(:service) { described_class.new(internal_group, user, visibility_level: 99 ) }
+    before do
+      internal_group.add_user(user, Gitlab::Access::MASTER)
+    end
+
+    it "does not change permission level" do
+      service.execute
+      expect(internal_group.errors.count).to eq(1)
+    end
+  end
+end
diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..14cc20e529ab1791af7be5ff5f69f96390fdeedb
--- /dev/null
+++ b/spec/services/issues/move_service_spec.rb
@@ -0,0 +1,213 @@
+require 'spec_helper'
+
+describe Issues::MoveService, services: true do
+  let(:user) { create(:user) }
+  let(:author) { create(:user) }
+  let(:title) { 'Some issue' }
+  let(:description) { 'Some issue description' }
+  let(:old_project) { create(:project) }
+  let(:new_project) { create(:project) }
+
+  let(:old_issue) do
+    create(:issue, title: title, description: description,
+                   project: old_project, author: author)
+  end
+
+  let(:move_service) do
+    described_class.new(old_project, user)
+  end
+
+  shared_context 'user can move issue' do
+    before do
+      old_project.team << [user, :reporter]
+      new_project.team << [user, :reporter]
+    end
+  end
+
+  describe '#execute' do
+    shared_context 'issue move executed' do
+      let!(:new_issue) { move_service.execute(old_issue, new_project) }
+    end
+
+    context 'issue movable' do
+      include_context 'user can move issue'
+
+      context 'generic issue' do
+        include_context 'issue move executed'
+
+        it 'creates a new issue in a new project' do
+          expect(new_issue.project).to eq new_project
+        end
+
+        it 'rewrites issue title' do
+          expect(new_issue.title).to eq title
+        end
+
+        it 'rewrites issue description' do
+          expect(new_issue.description).to eq description
+        end
+
+        it 'adds system note to old issue at the end' do
+          expect(old_issue.notes.last.note).to match /^Moved to/
+        end
+
+        it 'adds system note to new issue at the end' do
+          expect(new_issue.notes.last.note).to match /^Moved from/
+        end
+
+        it 'closes old issue' do
+          expect(old_issue.closed?).to be true
+        end
+
+        it 'persists new issue' do
+          expect(new_issue.persisted?).to be true
+        end
+
+        it 'persists all changes' do
+          expect(old_issue.changed?).to be false
+          expect(new_issue.changed?).to be false
+        end
+
+        it 'preserves author' do
+          expect(new_issue.author).to eq author
+        end
+
+        it 'removes data that is invalid in new context' do
+          expect(new_issue.milestone).to be_nil
+          expect(new_issue.labels).to be_empty
+        end
+
+        it 'creates a new internal id for issue' do
+          expect(new_issue.iid).to be 1
+        end
+
+        it 'marks issue as moved' do
+          expect(old_issue.moved?).to eq true
+          expect(old_issue.moved_to).to eq new_issue
+        end
+      end
+
+      context 'issue with notes' do
+        context 'notes without references' do
+          let(:notes_params) do
+            [{ system: false, note: 'Some comment 1' },
+             { system: true, note: 'Some system note' },
+             { system: false, note: 'Some comment 2' }]
+          end
+
+          let(:notes_contents) { notes_params.map { |n| n[:note] } }
+
+          before do
+            note_params = { noteable: old_issue, project: old_project, author: author }
+            notes_params.each do |note|
+              create(:note, note_params.merge(note))
+            end
+          end
+
+          include_context 'issue move executed'
+
+          let(:all_notes) { new_issue.notes.order('id ASC') }
+          let(:system_notes) { all_notes.system }
+          let(:user_notes) { all_notes.user }
+
+          it 'rewrites existing notes in valid order' do
+            expect(all_notes.pluck(:note).first(3)).to eq notes_contents
+          end
+
+          it 'adds a system note about move after rewritten notes' do
+            expect(system_notes.last.note).to match /^Moved from/
+          end
+
+          it 'preserves orignal author of comment' do
+            expect(user_notes.pluck(:author_id)).to all(eq(author.id))
+          end
+
+          it 'preserves time when note has been created at' do
+            expect(old_issue.notes.first.created_at)
+              .to eq new_issue.notes.first.created_at
+          end
+        end
+
+        context 'notes with references' do
+          before do
+            create(:merge_request, source_project: old_project)
+            create(:note, noteable: old_issue, project: old_project, author: author,
+                          note: 'Note with reference to merge request !1')
+          end
+
+          include_context 'issue move executed'
+          let(:new_note) { new_issue.notes.first }
+
+          it 'rewrites references using a cross reference to old project' do
+            expect(new_note.note)
+              .to eq "Note with reference to merge request #{old_project.to_reference}!1"
+          end
+        end
+      end
+
+      describe 'rewritting references' do
+        include_context 'issue move executed'
+
+        context 'issue reference' do
+          let(:another_issue) { create(:issue, project: old_project) }
+          let(:description) { "Some description #{another_issue.to_reference}" }
+
+          it 'rewrites referenced issues creating cross project reference' do
+            expect(new_issue.description)
+              .to eq "Some description #{old_project.to_reference}#{another_issue.to_reference}"
+          end
+        end
+      end
+
+      context 'moving to same project' do
+        let(:new_project) { old_project }
+
+        it 'raises error' do
+          expect { move_service.execute(old_issue, new_project) }
+            .to raise_error(StandardError, /Cannot move issue/)
+        end
+      end
+    end
+
+    describe 'move permissions' do
+      let(:move) { move_service.execute(old_issue, new_project) }
+
+      context 'user is reporter in both projects' do
+        include_context 'user can move issue'
+        it { expect { move }.to_not raise_error }
+      end
+
+      context 'user is reporter only in new project' do
+        before { new_project.team << [user, :reporter] }
+        it { expect { move }.to raise_error(StandardError, /permissions/) }
+      end
+
+      context 'user is reporter only in old project' do
+        before { old_project.team << [user, :reporter] }
+        it { expect { move }.to raise_error(StandardError, /permissions/) }
+      end
+
+      context 'user is reporter in one project and guest in another' do
+        before do
+          new_project.team << [user, :guest]
+          old_project.team << [user, :reporter]
+        end
+
+        it { expect { move }.to raise_error(StandardError, /permissions/) }
+      end
+
+      context 'issue has already been moved' do
+        include_context 'user can move issue'
+
+        let(:moved_to_issue) { create(:issue) }
+
+        let(:old_issue) do
+          create(:issue, project: old_project, author: author,
+                         moved_to: moved_to_issue)
+        end
+
+        it { expect { move }.to raise_error(StandardError, /permissions/) }
+      end
+    end
+  end
+end
diff --git a/spec/services/projects/autocomplete_service_spec.rb b/spec/services/projects/autocomplete_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6108c26a78b862808dacd7b9b1d80e6757df8f5f
--- /dev/null
+++ b/spec/services/projects/autocomplete_service_spec.rb
@@ -0,0 +1,79 @@
+require 'spec_helper'
+
+describe Projects::AutocompleteService, services: true do
+  describe '#issues' do
+    describe 'confidential issues' do
+      let(:author) { create(:user) }
+      let(:assignee) { create(:user) }
+      let(:non_member) { create(:user) }
+      let(:member) { create(:user) }
+      let(:admin) { create(:admin) }
+      let(:project) { create(:empty_project, :public) }
+      let!(:issue) { create(:issue, project: project, title: 'Issue 1') }
+      let!(:security_issue_1) { create(:issue, :confidential, project: project, title: 'Security issue 1', author: author) }
+      let!(:security_issue_2) { create(:issue, :confidential, title: 'Security issue 2', project: project, assignee: assignee) }
+
+      it 'should not list project confidential issues for guests' do
+        autocomplete = described_class.new(project, nil)
+        issues = autocomplete.issues.map(&:iid)
+
+        expect(issues).to include issue.iid
+        expect(issues).not_to include security_issue_1.iid
+        expect(issues).not_to include security_issue_2.iid
+        expect(issues.count).to eq 1
+      end
+
+      it 'should not list project confidential issues for non project members' do
+        autocomplete = described_class.new(project, non_member)
+        issues = autocomplete.issues.map(&:iid)
+
+        expect(issues).to include issue.iid
+        expect(issues).not_to include security_issue_1.iid
+        expect(issues).not_to include security_issue_2.iid
+        expect(issues.count).to eq 1
+      end
+
+      it 'should list project confidential issues for author' do
+        autocomplete = described_class.new(project, author)
+        issues = autocomplete.issues.map(&:iid)
+
+        expect(issues).to include issue.iid
+        expect(issues).to include security_issue_1.iid
+        expect(issues).not_to include security_issue_2.iid
+        expect(issues.count).to eq 2
+      end
+
+      it 'should list project confidential issues for assignee' do
+        autocomplete = described_class.new(project, assignee)
+        issues = autocomplete.issues.map(&:iid)
+
+        expect(issues).to include issue.iid
+        expect(issues).not_to include security_issue_1.iid
+        expect(issues).to include security_issue_2.iid
+        expect(issues.count).to eq 2
+      end
+
+      it 'should list project confidential issues for project members' do
+        project.team << [member, :developer]
+
+        autocomplete = described_class.new(project, member)
+        issues = autocomplete.issues.map(&:iid)
+
+        expect(issues).to include issue.iid
+        expect(issues).to include security_issue_1.iid
+        expect(issues).to include security_issue_2.iid
+        expect(issues.count).to eq 3
+      end
+
+      it 'should list all project issues for admin' do
+        autocomplete = described_class.new(project, admin)
+        issues = autocomplete.issues.map(&:iid)
+
+        expect(issues).to include issue.iid
+        expect(issues).to include security_issue_1.iid
+        expect(issues).to include security_issue_2.iid
+        expect(issues.count).to eq 3
+      end
+    end
+  end
+end
diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb
index 93bf1b81fbe65017bea26ec11bb554cd45798dbc..4c5ced7e746a29bb00fe8bcbd9533c9b30ef39c2 100644
--- a/spec/services/projects/housekeeping_service_spec.rb
+++ b/spec/services/projects/housekeeping_service_spec.rb
@@ -12,7 +12,7 @@ describe Projects::HousekeepingService do
 
     it 'enqueues a sidekiq job' do
       expect(subject).to receive(:try_obtain_lease).and_return(true)
-      expect(GitlabShellWorker).to receive(:perform_async).with(:gc, project.path_with_namespace)
+      expect(GitlabShellOneShotWorker).to receive(:perform_async).with(:gc, project.path_with_namespace)
 
       subject.execute
       expect(project.pushes_since_gc).to eq(0)
@@ -20,7 +20,7 @@ describe Projects::HousekeepingService do
 
     it 'does not enqueue a job when no lease can be obtained' do
       expect(subject).to receive(:try_obtain_lease).and_return(false)
-      expect(GitlabShellWorker).not_to receive(:perform_async)
+      expect(GitlabShellOneShotWorker).not_to receive(:perform_async)
 
       expect { subject.execute }.to raise_error(Projects::HousekeepingService::LeaseTaken)
       expect(project.pushes_since_gc).to eq(0)
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 8e6292014d4fddd13a4191a79bf23014c5fb7dd1..240eae10052088ef00ff116c9b135a7621a6c320 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -453,6 +453,59 @@ describe SystemNoteService, services: true do
     end
   end
 
+  describe '.noteable_moved' do
+    let(:new_project) { create(:project) }
+    let(:new_noteable) { create(:issue, project: new_project) }
+
+    subject do
+      described_class.noteable_moved(noteable, project, new_noteable, author, direction: direction)
+    end
+
+    shared_examples 'cross project mentionable' do
+      include GitlabMarkdownHelper
+
+      it 'should contain cross reference to new noteable' do
+        expect(subject.note).to include cross_project_reference(new_project, new_noteable)
+      end
+
+      it 'should mention referenced noteable' do
+        expect(subject.note).to include new_noteable.to_reference
+      end
+
+      it 'should mention referenced project' do
+        expect(subject.note).to include new_project.to_reference
+      end
+    end
+
+    context 'moved to' do
+      let(:direction) { :to }
+
+      it_behaves_like 'cross project mentionable'
+
+      it 'should notify about noteable being moved to' do
+        expect(subject.note).to match /Moved to/
+      end
+    end
+
+    context 'moved from' do
+      let(:direction) { :from }
+
+      it_behaves_like 'cross project mentionable'
+
+      it 'should notify about noteable being moved from' do
+        expect(subject.note).to match /Moved from/
+      end
+    end
+
+    context 'invalid direction' do
+      let(:direction) { :invalid }
+
+      it 'should raise error' do
+        expect { subject }.to raise_error StandardError, /Invalid direction/
+      end
+    end
+  end
+
   include JiraServiceHelper
 
   describe 'JIRA integration' do
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index 96420acb31dc6e473edea2106652f7d7059221d9..b4728807b8b892e87c8da5db95772765bff90068 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -148,8 +148,13 @@ describe TodoService, services: true do
         should_not_create_todo(user: stranger, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
       end
 
-      it 'does not create todo when leaving a note on commit' do
-        should_not_create_any_todo { service.new_note(note_on_commit, john_doe) }
+      it 'creates a todo for each valid mentioned user when leaving a note on commit' do
+        service.new_note(note_on_commit, john_doe)
+
+        should_create_todo(user: michael, 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_not_create_todo(user: stranger, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
       end
 
       it 'does not create todo when leaving a note on snippet' do
diff --git a/spec/services/update_snippet_service_spec.rb b/spec/services/update_snippet_service_spec.rb
index 48d114896d0ff5f8a33c21390053e0d43499f697..37c2e861362b6b65b661412f29371e751b1100ad 100644
--- a/spec/services/update_snippet_service_spec.rb
+++ b/spec/services/update_snippet_service_spec.rb
@@ -25,7 +25,7 @@ describe UpdateSnippetService, services: true do
       update_snippet(@project, @user, @snippet, @opts)
       expect(@snippet.errors.messages).to have_key(:visibility_level)
       expect(@snippet.errors.messages[:visibility_level].first).to(
-        match('Public visibility has been restricted')
+        match('has been restricted')
       )
       expect(@snippet.visibility_level).to eq(old_visibility)
     end
diff --git a/spec/support/matchers/access_matchers.rb b/spec/support/matchers/access_matchers.rb
index 4e007c777e3f5c13b5b2f6082d93ebfb84738743..0497e39186006381781a6743c3a47805b0b39860 100644
--- a/spec/support/matchers/access_matchers.rb
+++ b/spec/support/matchers/access_matchers.rb
@@ -28,7 +28,7 @@ module AccessMatchers
     if user.kind_of?(User)
       # User#inspect displays too much information for RSpec's description
       # messages
-      "be #{type} for supplied User"
+      "be #{type} for the specified user"
     else
       "be #{type} for #{user}"
     end
diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb
index fce91015fd4b9c2a20d2e053834cffe31473fbad..e876d44c166dc384ebc21c8509638b7a532d88f0 100644
--- a/spec/support/mentionable_shared_examples.rb
+++ b/spec/support/mentionable_shared_examples.rb
@@ -52,6 +52,8 @@ shared_context 'mentionable context' do
     end
 
     set_mentionable_text.call(ref_string)
+
+    project.team << [author, :developer]
   end
 end
 
diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb
index 172537474ee85bfc93ee415f4300e8558bd85faa..4ef05eb29d2b3f1c95f7b97bd7ed92325b0aa2ec 100644
--- a/spec/workers/repository_fork_worker_spec.rb
+++ b/spec/workers/repository_fork_worker_spec.rb
@@ -3,12 +3,17 @@ require 'spec_helper'
 describe RepositoryForkWorker do
   let(:project) { create(:project) }
   let(:fork_project) { create(:project, forked_from_project: project) }
+  let(:shell) { Gitlab::Shell.new }
 
   subject { RepositoryForkWorker.new }
 
+  before do
+    allow(subject).to receive(:gitlab_shell).and_return(shell)
+  end
+
   describe "#perform" do
     it "creates a new repository from a fork" do
-      expect_any_instance_of(Gitlab::Shell).to receive(:fork_repository).with(
+      expect(shell).to receive(:fork_repository).with(
         project.path_with_namespace,
         fork_project.namespace.path
       ).and_return(true)
@@ -19,20 +24,26 @@ describe RepositoryForkWorker do
         fork_project.namespace.path)
     end
 
-    it 'flushes the empty caches' do
-      expect_any_instance_of(Gitlab::Shell).to receive(:fork_repository).
+    it 'flushes various caches' do
+      expect(shell).to receive(:fork_repository).
         with(project.path_with_namespace, fork_project.namespace.path).
         and_return(true)
 
       expect_any_instance_of(Repository).to receive(:expire_emptiness_caches).
         and_call_original
 
+      expect_any_instance_of(Repository).to receive(:expire_exists_cache).
+        and_call_original
+
       subject.perform(project.id, project.path_with_namespace,
                       fork_project.namespace.path)
     end
 
     it "handles bad fork" do
-      expect_any_instance_of(Gitlab::Shell).to receive(:fork_repository).and_return(false)
+      expect(shell).to receive(:fork_repository).and_return(false)
+
+      expect(subject.logger).to receive(:error)
+
       subject.perform(
         project.id,
         project.path_with_namespace,