From d471755f689ecad1180c463cf56ac45c62b3362f Mon Sep 17 00:00:00 2001 From: bujjibabukatta Date: Mon, 15 Jun 2026 16:31:39 +0530 Subject: [PATCH 1/2] fix(gitlab): use diff_stats instead of changes_count for correct MR size (fixes #8888) --- .../add_additions_deletions_to_mr.go | 32 +++++++++++++++++++ .../models/migrationscripts/register.go | 1 + backend/plugins/gitlab/models/mr.go | 2 ++ .../gitlab/tasks/mr_detail_collector.go | 1 + backend/plugins/gitlab/tasks/mr_extractor.go | 9 ++++++ 5 files changed, 45 insertions(+) create mode 100644 backend/plugins/gitlab/models/migrationscripts/add_additions_deletions_to_mr.go diff --git a/backend/plugins/gitlab/models/migrationscripts/add_additions_deletions_to_mr.go b/backend/plugins/gitlab/models/migrationscripts/add_additions_deletions_to_mr.go new file mode 100644 index 00000000000..e4691a5dab8 --- /dev/null +++ b/backend/plugins/gitlab/models/migrationscripts/add_additions_deletions_to_mr.go @@ -0,0 +1,32 @@ +package migrationscripts + +import ( + "github.com/apache/incubator-devlake/core/context" + "github.com/apache/incubator-devlake/core/errors" + "github.com/apache/incubator-devlake/core/plugin" +) + +var _ plugin.MigrationScript = (*addAdditionsDeletionsToMr)(nil) + +type mrAdditionsDeletions struct { + Additions int + Deletions int +} + +func (mrAdditionsDeletions) TableName() string { + return "_tool_gitlab_merge_requests" +} + +type addAdditionsDeletionsToMr struct{} + +func (*addAdditionsDeletionsToMr) Up(basicRes context.BasicRes) errors.Error { + return errors.Convert(basicRes.GetDal().AutoMigrate(&mrAdditionsDeletions{})) +} + +func (*addAdditionsDeletionsToMr) Version() uint64 { + return 20260615000001 +} + +func (*addAdditionsDeletionsToMr) Name() string { + return "gitlab: add additions and deletions to merge requests" +} \ No newline at end of file diff --git a/backend/plugins/gitlab/models/migrationscripts/register.go b/backend/plugins/gitlab/models/migrationscripts/register.go index 30a76f63ed9..e684c953064 100644 --- a/backend/plugins/gitlab/models/migrationscripts/register.go +++ b/backend/plugins/gitlab/models/migrationscripts/register.go @@ -53,5 +53,6 @@ func All() []plugin.MigrationScript { new(changeIssueComponentType), new(addIsChildToPipelines240906), new(addPrSizeExcludedFileExtensions), + new(addAdditionsDeletionsToMr), } } diff --git a/backend/plugins/gitlab/models/mr.go b/backend/plugins/gitlab/models/mr.go index c25df4ba07f..57bf22a9c52 100644 --- a/backend/plugins/gitlab/models/mr.go +++ b/backend/plugins/gitlab/models/mr.go @@ -51,6 +51,8 @@ type GitlabMergeRequest struct { AuthorUsername string `gorm:"type:varchar(255)"` AuthorUserId int Component string `gorm:"type:varchar(255)"` + Additions int `gorm:"comment:Lines added in this MR diff only"` + Deletions int `gorm:"comment:Lines deleted in this MR diff only"` common.NoPKModel } diff --git a/backend/plugins/gitlab/tasks/mr_detail_collector.go b/backend/plugins/gitlab/tasks/mr_detail_collector.go index bfb64f031b5..d20e96a1ec9 100644 --- a/backend/plugins/gitlab/tasks/mr_detail_collector.go +++ b/backend/plugins/gitlab/tasks/mr_detail_collector.go @@ -62,6 +62,7 @@ func CollectApiMergeRequestDetails(taskCtx plugin.SubTaskContext) errors.Error { Query: func(reqData *helper.RequestData) (url.Values, errors.Error) { query := url.Values{} query.Set("with_stats", "true") + query.Set("include_diff_stats", "true") return query, nil }, ResponseParser: GetOneRawMessageFromResponse, diff --git a/backend/plugins/gitlab/tasks/mr_extractor.go b/backend/plugins/gitlab/tasks/mr_extractor.go index 3a8457b0cf0..6f559909eed 100644 --- a/backend/plugins/gitlab/tasks/mr_extractor.go +++ b/backend/plugins/gitlab/tasks/mr_extractor.go @@ -65,6 +65,13 @@ type MergeRequestRes struct { Assignees []Assignee FirstCommentTime common.Iso8601Time Labels []string `json:"labels"` + DiffStats struct { + + Additions int `json:"additions"` + + Deletions int `json:"deletions"` + + } `json:"diff_stats"` } type Reviewer struct { @@ -245,6 +252,8 @@ func convertMergeRequest(mr *MergeRequestRes) (*models.GitlabMergeRequest, error MergedByUsername: mr.MergedBy.Username, AuthorUsername: mr.Author.Username, AuthorUserId: mr.Author.Id, + Additions: mr.DiffStats.Additions, + Deletions: mr.DiffStats.Deletions, } return gitlabMergeRequest, nil } From 60922bf0b84e7708f52b4271803facf4be9c4743 Mon Sep 17 00:00:00 2001 From: bujjibabukatta Date: Wed, 24 Jun 2026 17:13:09 +0530 Subject: [PATCH 2/2] fix(gitlab): handle missing diff_stats for older GitLab instances --- backend/plugins/gitlab/tasks/mr_extractor.go | 21 ++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/backend/plugins/gitlab/tasks/mr_extractor.go b/backend/plugins/gitlab/tasks/mr_extractor.go index 6f559909eed..a364bf5a614 100644 --- a/backend/plugins/gitlab/tasks/mr_extractor.go +++ b/backend/plugins/gitlab/tasks/mr_extractor.go @@ -65,13 +65,11 @@ type MergeRequestRes struct { Assignees []Assignee FirstCommentTime common.Iso8601Time Labels []string `json:"labels"` - DiffStats struct { - + DiffStats *struct { Additions int `json:"additions"` - Deletions int `json:"deletions"` - } `json:"diff_stats"` + ChangesCount string `json:"changes_count"` } type Reviewer struct { @@ -252,8 +250,19 @@ func convertMergeRequest(mr *MergeRequestRes) (*models.GitlabMergeRequest, error MergedByUsername: mr.MergedBy.Username, AuthorUsername: mr.Author.Username, AuthorUserId: mr.Author.Id, - Additions: mr.DiffStats.Additions, - Deletions: mr.DiffStats.Deletions, + } + // Use diff_stats when available (GitLab 13.0+); fall back to changes_count + // for older self-managed instances that don't support include_diff_stats + if mr.DiffStats != nil && (mr.DiffStats.Additions > 0 || mr.DiffStats.Deletions > 0) { + gitlabMergeRequest.Additions = mr.DiffStats.Additions + gitlabMergeRequest.Deletions = mr.DiffStats.Deletions + } + else if mr.ChangesCount != "" && mr.ChangesCount != "0" { + count, err := strconv.Atoi(mr.ChangesCount) + if err == nil { + // changes_count is cumulative (bug source), but better than zero + gitlabMergeRequest.Additions = count + } } return gitlabMergeRequest, nil }