Commit 7c0718cd authored by Rémy Coutable's avatar Rémy Coutable

Merge branch '51083-fix-move-api' into 'master'

Commits API: Preserve file content in move operations if unspecified

Closes #52974 et #51083

See merge request gitlab-org/gitlab-ce!23387
parents 938b891f 14076062
...@@ -432,7 +432,7 @@ group :ed25519 do ...@@ -432,7 +432,7 @@ group :ed25519 do
end end
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly-proto', '~> 1.1.0', require: 'gitaly' gem 'gitaly-proto', '~> 1.2.0', require: 'gitaly'
gem 'grpc', '~> 1.15.0' gem 'grpc', '~> 1.15.0'
gem 'google-protobuf', '~> 3.6' gem 'google-protobuf', '~> 3.6'
......
...@@ -273,7 +273,7 @@ GEM ...@@ -273,7 +273,7 @@ GEM
gettext_i18n_rails (>= 0.7.1) gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gitaly-proto (1.1.0) gitaly-proto (1.2.0)
grpc (~> 1.0) grpc (~> 1.0)
github-markup (1.7.0) github-markup (1.7.0)
gitlab-default_value_for (3.1.1) gitlab-default_value_for (3.1.1)
...@@ -1006,7 +1006,7 @@ DEPENDENCIES ...@@ -1006,7 +1006,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3) gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 1.1.0) gitaly-proto (~> 1.2.0)
github-markup (~> 1.7.0) github-markup (~> 1.7.0)
gitlab-default_value_for (~> 3.1.1) gitlab-default_value_for (~> 3.1.1)
gitlab-markup (~> 1.6.5) gitlab-markup (~> 1.6.5)
......
...@@ -272,7 +272,7 @@ GEM ...@@ -272,7 +272,7 @@ GEM
gettext_i18n_rails (>= 0.7.1) gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gitaly-proto (1.1.0) gitaly-proto (1.2.0)
grpc (~> 1.0) grpc (~> 1.0)
github-markup (1.7.0) github-markup (1.7.0)
gitlab-markup (1.6.5) gitlab-markup (1.6.5)
...@@ -998,7 +998,7 @@ DEPENDENCIES ...@@ -998,7 +998,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3) gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 1.1.0) gitaly-proto (~> 1.2.0)
github-markup (~> 1.7.0) github-markup (~> 1.7.0)
gitlab-markup (~> 1.6.5) gitlab-markup (~> 1.6.5)
gitlab-sidekiq-fetcher gitlab-sidekiq-fetcher
......
...@@ -8,6 +8,7 @@ module Files ...@@ -8,6 +8,7 @@ module Files
transformer = Lfs::FileTransformer.new(project, @branch_name) transformer = Lfs::FileTransformer.new(project, @branch_name)
actions = actions_after_lfs_transformation(transformer, params[:actions]) actions = actions_after_lfs_transformation(transformer, params[:actions])
actions = transform_move_actions(actions)
commit_actions!(actions) commit_actions!(actions)
end end
...@@ -26,6 +27,16 @@ module Files ...@@ -26,6 +27,16 @@ module Files
end end
end end
# When moving a file, `content: nil` means "use the contents of the previous
# file", while `content: ''` means "move the file and set it to empty"
def transform_move_actions(actions)
actions.map do |action|
action[:infer_content] = true if action[:content].nil?
action
end
end
def commit_actions!(actions) def commit_actions!(actions)
repository.multi_action( repository.multi_action(
current_user, current_user,
......
---
title: 'Commits API: Preserve file content in move operations if unspecified'
merge_request: 23387
author:
type: fixed
...@@ -87,7 +87,7 @@ POST /projects/:id/repository/commits ...@@ -87,7 +87,7 @@ POST /projects/:id/repository/commits
| `action` | string | yes | The action to perform, `create`, `delete`, `move`, `update`, `chmod`| | `action` | string | yes | The action to perform, `create`, `delete`, `move`, `update`, `chmod`|
| `file_path` | string | yes | Full path to the file. Ex. `lib/class.rb` | | `file_path` | string | yes | Full path to the file. Ex. `lib/class.rb` |
| `previous_path` | string | no | Original full path to the file being moved. Ex. `lib/class1.rb`. Only considered for `move` action. | | `previous_path` | string | no | Original full path to the file being moved. Ex. `lib/class1.rb`. Only considered for `move` action. |
| `content` | string | no | File content, required for all except `delete` and `chmod`. Optional for `move` | | `content` | string | no | File content, required for all except `delete`, `chmod`, and `move`. Move actions that do not specify `content` will preserve the existing file content, and any other value of `content` will overwrite the file content. |
| `encoding` | string | no | `text` or `base64`. `text` is default. | | `encoding` | string | no | `text` or `base64`. `text` is default. |
| `last_commit_id` | string | no | Last known file commit id. Will be only considered in update, move and delete actions. | | `last_commit_id` | string | no | Last known file commit id. Will be only considered in update, move and delete actions. |
| `execute_filemode` | boolean | no | When `true/false` enables/disables the execute flag on the file. Only considered for `chmod` action. | | `execute_filemode` | boolean | no | When `true/false` enables/disables the execute flag on the file. Only considered for `chmod` action. |
......
...@@ -385,7 +385,8 @@ module Gitlab ...@@ -385,7 +385,8 @@ module Gitlab
file_path: encode_binary(action[:file_path]), file_path: encode_binary(action[:file_path]),
previous_path: encode_binary(action[:previous_path]), previous_path: encode_binary(action[:previous_path]),
base64_content: action[:encoding] == 'base64', base64_content: action[:encoding] == 'base64',
execute_filemode: !!action[:execute_filemode] execute_filemode: !!action[:execute_filemode],
infer_content: !!action[:infer_content]
) )
rescue RangeError rescue RangeError
raise ArgumentError, "Unknown action '#{action[:action]}'" raise ArgumentError, "Unknown action '#{action[:action]}'"
......
...@@ -122,26 +122,47 @@ describe Files::MultiService do ...@@ -122,26 +122,47 @@ describe Files::MultiService do
let(:action) { 'move' } let(:action) { 'move' }
let(:new_file_path) { 'files/ruby/new_popen.rb' } let(:new_file_path) { 'files/ruby/new_popen.rb' }
let(:result) { subject.execute }
let(:blob) { repository.blob_at_branch(branch_name, new_file_path) }
context 'when original file has been updated' do context 'when original file has been updated' do
before do before do
update_file(original_file_path) update_file(original_file_path)
end end
it 'rejects the commit' do it 'rejects the commit' do
results = subject.execute expect(result[:status]).to eq(:error)
expect(result[:message]).to match(original_file_path)
expect(results[:status]).to eq(:error)
expect(results[:message]).to match(original_file_path)
end end
end end
context 'when original file have not been updated' do context 'when original file has not been updated' do
it 'moves the file' do it 'moves the file' do
results = subject.execute expect(result[:status]).to eq(:success)
blob = project.repository.blob_at_branch(branch_name, new_file_path)
expect(results[:status]).to eq(:success)
expect(blob).to be_present expect(blob).to be_present
expect(blob.data).to eq(file_content)
end
context 'when content is nil' do
let(:file_content) { nil }
it 'moves the existing content untouched' do
original_content = repository.blob_at_branch(branch_name, original_file_path).data
expect(result[:status]).to eq(:success)
expect(blob).to be_present
expect(blob.data).to eq(original_content)
end
end
context 'when content is an empty string' do
let(:file_content) { '' }
it 'moves the file and empties it' do
expect(result[:status]).to eq(:success)
expect(blob).not_to be_nil
expect(blob.data).to eq('')
end
end end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment