diff --git a/CHANGELOG b/CHANGELOG
index aa9f93274ec00e62c69dde77b10527d5e601c0ab..a5d26db7626ccda1f0935253fb1c46ce1abfcabd 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -5,6 +5,7 @@ v 8.11.0 (unreleased)
 v 8.10.0 (unreleased)
   - Fix profile activity heatmap to show correct day name (eanplatter)
   - Expose {should,force}_remove_source_branch (Ben Boeckel)
+  - Add the functionality to be able to rename a file. !5049 (tiagonbotelho)
   - Disable PostgreSQL statement timeout during migrations
   - Fix projects dropdown loading performance with a simplified api cal. !5113 (tiagonbotelho)
   - Fix commit builds API, return all builds for all pipelines for given commit. !4849
diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb
index dacb5679dd300fde4a034ba762960ee5a41610e7..f2b8f297bc27886a536478ba77aeb82304cfaa56 100644
--- a/app/controllers/concerns/creates_commit.rb
+++ b/app/controllers/concerns/creates_commit.rb
@@ -7,7 +7,8 @@ module CreatesCommit
     commit_params = @commit_params.merge(
       source_project: @project,
       source_branch: @ref,
-      target_branch: @target_branch
+      target_branch: @target_branch,
+      previous_path: @previous_path
     )
 
     result = service.new(@tree_edit_project, current_user, commit_params).execute
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 5356fdf010df794cc86314602ecd9c21a31b809a..eda3727a28de2ebc788e41fbc12f86dd46831b79 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -38,6 +38,12 @@ class Projects::BlobController < Projects::ApplicationController
   end
 
   def update
+    if params[:file_path].present?
+      @previous_path = @path
+      @path = params[:file_path]
+      @commit_params[:file_path] = @path
+    end
+
     after_edit_path =
       if from_merge_request && @target_branch == @ref
         diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
diff --git a/app/models/repository.rb b/app/models/repository.rb
index c187bad39ad60e11aa858bbb6b29f7ef9b22859a..1a2ac90da51386212b8d19737727403d9eeb8c68 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -733,6 +733,33 @@ class Repository
     end
   end
 
+  def update_file(user, path, content, branch:, previous_path:, message:)
+    commit_with_hooks(user, branch) do |ref|
+      committer = user_to_committer(user)
+      options = {}
+      options[:committer] = committer
+      options[:author] = committer
+      options[:commit] = {
+        message: message,
+        branch: ref,
+        update_ref: false
+      }
+
+      options[:file] = {
+        content: content,
+        path: path,
+        update: true
+      }
+
+      if previous_path
+        options[:file][:previous_path] = previous_path
+        Gitlab::Git::Blob.rename(raw_repository, options)
+      else
+        Gitlab::Git::Blob.commit(raw_repository, options)
+      end
+    end
+  end
+
   def remove_file(user, path, message, branch)
     commit_with_hooks(user, branch) do |ref|
       committer = user_to_committer(user)
diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb
index 55da949f56aee8a4561d83a50455fe625ee4c164..c4a206f785e3b493b30a6b1abc74ec5ddf668902 100644
--- a/app/services/files/base_service.rb
+++ b/app/services/files/base_service.rb
@@ -9,12 +9,14 @@ module Files
 
       @commit_message = params[:commit_message]
       @file_path      = params[:file_path]
+      @previous_path  = params[:previous_path]
       @file_content   = if params[:file_content_encoding] == 'base64'
                           Base64.decode64(params[:file_content])
                         else
                           params[:file_content]
                         end
 
+      # Validate parameters
       validate
 
       # Create new branch if it different from source_branch
diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb
index 1960dc7d949c1abb2612d024227da98050a1c5e9..8d2b5083179e0ed50c507a355e9067a6d5d05d67 100644
--- a/app/services/files/update_service.rb
+++ b/app/services/files/update_service.rb
@@ -3,7 +3,10 @@ require_relative "base_service"
 module Files
   class UpdateService < Files::BaseService
     def commit
-      repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch, true)
+      repository.update_file(current_user, @file_path, @file_content,
+                             branch: @target_branch,
+                             previous_path: @previous_path,
+                             message: @commit_message)
     end
   end
 end
diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml
index 29c7d45074a2241d5f20bb0a78b4443a0cec8184..ff379bafb26759800baee0a81780423ada586aa7 100644
--- a/app/views/projects/blob/_editor.html.haml
+++ b/app/views/projects/blob/_editor.html.haml
@@ -4,7 +4,9 @@
       = icon('code-fork')
       = ref
     %span.editor-file-name
-      = @path
+      - if current_action?(:edit) || current_action?(:update)
+        = text_field_tag 'file_path', (params[:file_path] || @path),
+                                      class: 'form-control new-file-path'
 
     - if current_action?(:new) || current_action?(:create)
       %span.editor-file-name
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index e14cec589fe4bbdb92227c22bdea2c37d4f161f5..110df6bbd22cc80579445e3c4351c8262f3590d3 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -130,6 +130,36 @@ describe Repository, models: true do
     end
   end
 
+  describe :commit_file do
+    it 'commits change to a file successfully' do
+      expect do
+        repository.commit_file(user, 'CHANGELOG', 'Changelog!',
+                              'Updates file content',
+                              'master', true)
+      end.to change { repository.commits('master').count }.by(1)
+
+      blob = repository.blob_at('master', 'CHANGELOG')
+
+      expect(blob.data).to eq('Changelog!')
+    end
+  end
+
+  describe :update_file do
+    it 'updates filename successfully' do
+      expect do
+        repository.update_file(user, 'NEWLICENSE', 'Copyright!',
+                                     branch: 'master',
+                                     previous_path: 'LICENSE',
+                                     message: 'Changes filename')
+      end.to change { repository.commits('master').count }.by(1)
+
+      files = repository.ls_files('master')
+
+      expect(files).not_to include('LICENSE')
+      expect(files).to include('NEWLICENSE')
+    end
+  end
+
   describe "search_files" do
     let(:results) { repository.search_files('feature', 'master') }
     subject { results }