diff --git a/Gemfile b/Gemfile index 1901455af077069ed38379e720fb3ed257b15b99..5a3a6791b298d77946c51f3c941d79818e56ec59 100644 --- a/Gemfile +++ b/Gemfile @@ -446,7 +446,7 @@ group :ed25519 do end # Gitaly GRPC protocol definitions -gem 'gitaly', '~> 1.58.0' +gem 'gitaly', '~> 1.65.0' gem 'grpc', '~> 1.19.0' diff --git a/Gemfile.lock b/Gemfile.lock index 7b450c262dab39e7573b120606b06179158eeee7..3142ba094cdc6d7f886abb3eed90f07a29e4fdd1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -358,7 +358,7 @@ GEM po_to_json (>= 1.0.0) rails (>= 3.2.0) git (1.5.0) - gitaly (1.58.0) + gitaly (1.65.0) grpc (~> 1.0) github-markup (1.7.0) gitlab-labkit (0.5.2) @@ -1168,7 +1168,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.3) - gitaly (~> 1.58.0) + gitaly (~> 1.65.0) github-markup (~> 1.7.0) gitlab-labkit (~> 0.5) gitlab-license (~> 1.0) diff --git a/app/models/repository.rb b/app/models/repository.rb index f084a314392f24c5c6751cf347be2d08dcbbe0f1..96b1b55e2b18e3b3025d387e3aba614ec9e82ab6 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -133,18 +133,28 @@ def commits_by(oids:) end end - def commits(ref = nil, path: nil, limit: nil, offset: nil, skip_merges: false, after: nil, before: nil, all: nil) + # the opts are: + # - :path + # - :limit + # - :offset + # - :skip_merges + # - :after + # - :before + # - :all + # - :first_parent + def commits(ref = nil, opts = {}) options = { repo: raw_repository, ref: ref, - path: path, - limit: limit, - offset: offset, - after: after, - before: before, - follow: Array(path).length == 1, - skip_merges: skip_merges, - all: all + path: opts[:path], + follow: Array(opts[:path]).length == 1, + limit: opts[:limit], + offset: opts[:offset], + skip_merges: !!opts[:skip_merges], + after: opts[:after], + before: opts[:before], + all: !!opts[:all], + first_parent: !!opts[:first_parent] } commits = Gitlab::Git::Commit.where(options) diff --git a/changelogs/unreleased/add-first-parent-to-find-commits.yml b/changelogs/unreleased/add-first-parent-to-find-commits.yml new file mode 100644 index 0000000000000000000000000000000000000000..076eed90f68cac18952baf3c72bff7fe28b99a30 --- /dev/null +++ b/changelogs/unreleased/add-first-parent-to-find-commits.yml @@ -0,0 +1,5 @@ +--- +title: Add first_parent option to list commits api +merge_request: 32410 +author: jhenkens +type: added diff --git a/doc/api/commits.md b/doc/api/commits.md index b41409b4b927b463768a07302e6d70157e7ddb24..3927a4bbc6207b8066cf6fe7ee27fba13c99cf51 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -11,12 +11,13 @@ GET /projects/:id/repository/commits | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user -| `ref_name` | string | no | The name of a repository branch or tag or if not given the default branch | +| `ref_name` | string | no | The name of a repository branch, tag or revision range, or if not given the default branch | | `since` | string | no | Only commits after or on this date will be returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ | | `until` | string | no | Only commits before or on this date will be returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ | | `path` | string | no | The file path | | `all` | boolean | no | Retrieve every commit from the repository | | `with_stats` | boolean | no | Stats about each commit will be added to the response | +| `first_parent` | boolean | no | Follow only the first parent commit upon seeing a merge commit | ```bash curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/repository/commits" diff --git a/lib/api/commits.rb b/lib/api/commits.rb index a2f3e87ebd26b725401113591e2a060cc985928e..ffff40141de5e7b34ae92537564aeb0fe426e95d 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -37,6 +37,7 @@ def authorize_push_to_branch!(branch) optional :path, type: String, desc: 'The file path' optional :all, type: Boolean, desc: 'Every commit will be returned' optional :with_stats, type: Boolean, desc: 'Stats about each commit will be added to the response' + optional :first_parent, type: Boolean, desc: 'Only include the first parent of merges' use :pagination end get ':id/repository/commits' do @@ -47,6 +48,7 @@ def authorize_push_to_branch!(branch) offset = (params[:page] - 1) * params[:per_page] all = params[:all] with_stats = params[:with_stats] + first_parent = params[:first_parent] commits = user_project.repository.commits(ref, path: path, @@ -54,11 +56,12 @@ def authorize_push_to_branch!(branch) offset: offset, before: before, after: after, - all: all) + all: all, + first_parent: first_parent) commit_count = - if all || path || before || after - user_project.repository.count_commits(ref: ref, path: path, before: before, after: after, all: all) + if all || path || before || after || first_parent + user_project.repository.count_commits(ref: ref, path: path, before: before, after: after, all: all, first_parent: first_parent) else # Cacheable commit count. user_project.repository.commit_count_for_ref(ref) diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb index a80ce462ab00c3d900393909f90f46310e217047..5c79a9c5a259787e91f5d892c99287e1437dbcaa 100644 --- a/lib/gitlab/gitaly_client/commit_service.rb +++ b/lib/gitlab/gitaly_client/commit_service.rb @@ -140,7 +140,8 @@ def commit_count(ref, options = {}) request = Gitaly::CountCommitsRequest.new( repository: @gitaly_repo, revision: encode_binary(ref), - all: !!options[:all] + all: !!options[:all], + first_parent: !!options[:first_parent] ) request.after = Google::Protobuf::Timestamp.new(seconds: options[:after].to_i) if options[:after].present? request.before = Google::Protobuf::Timestamp.new(seconds: options[:before].to_i) if options[:before].present? @@ -325,6 +326,7 @@ def find_commits(options) follow: options[:follow], skip_merges: options[:skip_merges], all: !!options[:all], + first_parent: !!options[:first_parent], disable_walk: true # This option is deprecated. The 'walk' implementation is being removed. ) request.after = GitalyClient.timestamp(options[:after]) if options[:after] diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 6dc47e0e5019ce40c38b5fb350100563e1c78888..011b46c7f1af1c0fad3055de144779cfbd308ca2 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -279,7 +279,7 @@ def create_commit_with_invalid_utf8_path describe '#commits' do context 'when neither the all flag nor a ref are specified' do it 'returns every commit from default branch' do - expect(repository.commits(limit: 60).size).to eq(37) + expect(repository.commits(nil, limit: 60).size).to eq(37) end end @@ -320,7 +320,7 @@ def create_commit_with_invalid_utf8_path context "when 'all' flag is set" do it 'returns every commit from the repository' do - expect(repository.commits(all: true, limit: 60).size).to eq(60) + expect(repository.commits(nil, all: true, limit: 60).size).to eq(60) end end end diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 5e6ff40e8cf2f13e36417f91b1c46f9e76120664..90ff1d12bf14e88520bac45581355325366eaf5f 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -169,6 +169,18 @@ end end + context 'first_parent optional parameter' do + it 'returns all first_parent commits' do + commit_count = project.repository.count_commits(ref: SeedRepo::Commit::ID, first_parent: true) + + get api("/projects/#{project_id}/repository/commits", user), params: { ref_name: SeedRepo::Commit::ID, first_parent: 'true' } + + expect(response).to include_pagination_headers + expect(commit_count).to eq(12) + expect(response.headers['X-Total']).to eq(commit_count.to_s) + end + end + context 'with_stats optional parameter' do let(:project) { create(:project, :public, :repository) }