diff --git a/changelogs/unreleased/dz-rename-invalid-users.yml b/changelogs/unreleased/dz-rename-invalid-users.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f420b069531231d4f9d98b5a18f0d3635e2aac78
--- /dev/null
+++ b/changelogs/unreleased/dz-rename-invalid-users.yml
@@ -0,0 +1,4 @@
+---
+title: Rename users with namespace ending with .git
+merge_request: 8309
+author:
diff --git a/db/migrate/20161226122833_remove_dot_git_from_usernames.rb b/db/migrate/20161226122833_remove_dot_git_from_usernames.rb
new file mode 100644
index 0000000000000000000000000000000000000000..809b09feb849223274cdb6db76cfc02723e02e07
--- /dev/null
+++ b/db/migrate/20161226122833_remove_dot_git_from_usernames.rb
@@ -0,0 +1,87 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class RemoveDotGitFromUsernames < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+  include Gitlab::ShellAdapter
+
+  # Set this constant to true if this migration requires downtime.
+  DOWNTIME = false
+
+  def up
+    invalid_users.each do |user|
+      id = user['id']
+      namespace_id = user['namespace_id']
+      path_was = user['username']
+      path_was_wildcard = quote_string("#{path_was}/%")
+      path = quote_string(rename_path(path_was))
+
+      move_namespace(namespace_id, path_was, path)
+
+      execute "UPDATE routes SET path = '#{path}' WHERE source_type = 'Namespace' AND source_id = #{namespace_id}"
+      execute "UPDATE namespaces SET path = '#{path}' WHERE id = #{namespace_id}"
+      execute "UPDATE users SET username = '#{path}' WHERE id = #{id}"
+
+      select_all("SELECT id, path FROM routes WHERE path LIKE '#{path_was_wildcard}'").each do |route|
+        new_path = "#{path}/#{route['path'].split('/').last}"
+        execute "UPDATE routes SET path = '#{new_path}' WHERE id = #{route['id']}"
+      end
+    end
+  end
+
+  def down
+    # nothing to do here
+  end
+
+  private
+
+  def invalid_users
+    select_all("SELECT u.id, u.username, n.path AS namespace_path, n.id AS namespace_id FROM users u
+                INNER JOIN namespaces n ON n.owner_id = u.id
+                WHERE n.type is NULL AND n.path LIKE '%.git'")
+  end
+
+  def route_exists?(path)
+    select_all("SELECT id, path FROM routes WHERE path = '#{quote_string(path)}'").present?
+  end
+
+  # Accepts invalid path like test.git and returns test_git or
+  # test_git1 if test_git already taken
+  def rename_path(path)
+    # To stay closer with original name and reduce risk of duplicates
+    # we rename suffix instead of removing it
+    path = path.sub(/\.git\z/, '_git')
+
+    counter = 0
+    base = path
+
+    while route_exists?(path)
+      counter += 1
+      path = "#{base}#{counter}"
+    end
+
+    path
+  end
+
+  def move_namespace(namespace_id, path_was, path)
+    repository_storage_paths = select_all("SELECT distinct(repository_storage) FROM projects WHERE namespace_id = #{namespace_id}").map do |row|
+      Gitlab.config.repositories.storages[row['repository_storage']]
+    end.compact
+
+    # Move the namespace directory in all storages paths used by member projects
+    repository_storage_paths.each do |repository_storage_path|
+      # Ensure old directory exists before moving it
+      gitlab_shell.add_namespace(repository_storage_path, path_was)
+
+      unless gitlab_shell.mv_namespace(repository_storage_path, path_was, path)
+        Rails.logger.error "Exception moving path #{repository_storage_path} from #{path_was} to #{path}"
+
+        # if we cannot move namespace directory we should rollback
+        # db changes in order to prevent out of sync between db and fs
+        raise Exception.new('namespace directory cannot be moved')
+      end
+    end
+
+    Gitlab::UploadsTransfer.new.rename_namespace(path_was, path)
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 05b6c807660180e493eeb08ff8490f99db2d91b1..1fcb6bcfe60b9b6bfb8250e0015fbacd0eaea1e0 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: 20161221140236) do
+ActiveRecord::Schema.define(version: 20161226122833) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
diff --git a/spec/migrations/remove_dot_git_from_usernames.rb b/spec/migrations/remove_dot_git_from_usernames.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1b1d2adc46386cf0bd7c44162c90220faf1db5f0
--- /dev/null
+++ b/spec/migrations/remove_dot_git_from_usernames.rb
@@ -0,0 +1,29 @@
+# encoding: utf-8
+
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20161226122833_remove_dot_git_from_usernames.rb')
+
+describe RemoveDotGitFromUsernames do
+  let(:user) { create(:user) }
+
+  describe '#up' do
+    let(:migration) { described_class.new }
+
+    before do
+      namespace = user.namespace
+      namespace.path = 'test.git'
+      namespace.save!(validate: false)
+
+      user.username = 'test.git'
+      user.save!(validate: false)
+    end
+
+    it 'renames user with .git in username' do
+      migration.up
+
+      expect(user.reload.username).to eq('test_git')
+      expect(user.namespace.reload.path).to eq('test_git')
+      expect(user.namespace.route.path).to eq('test_git')
+    end
+  end
+end