Draft: feat(approvals): smarter approval resets for merge request rebases
Problem
When a merge request is rebased, all approvals are frequently reset even though the actual code changes are identical. This is caused by git patch-id including 3 context lines in its hash. When a rebase shifts the merge base, surrounding context lines change, producing a different patch-ID and triggering a false-positive approval reset.
Real-world impact (DevEx report): !225247 (merged) had all 5 approvals by Mar 5. A rebase on Mar 6 reset all approvals. The MR finally merged Mar 9 — 4 calendar days after approval, with ~10.5 hours of wasted CI compute and 4 dependent MRs blocked.
This is tracked as &544 (Smarter approval resets) — open since 2018, due Mar 13 2026.
Solution
Two independent, feature-flagged layers that preserve security guarantees:
Layer 1: Context-free patch-ID (Gitaly + Rails)
Compute git patch-id from a diff with zero context lines (-U0). The hash then depends only on actual added/removed lines, not surrounding code that shifts on rebase.
- Gitaly side:
!8535addsbool context_freetoGetPatchIDRequest - Rails side: passes
context_free: truethrough the client chain when theapproval_reset_zero_context_patch_idfeature flag is enabled - Security: The hash still reflects all code changes. A malicious commit produces a different patch-ID.
Layer 2: Rebase detection fast path (Rails)
Detects when a push is purely a rebase (no new functional commits) and skips the approval reset. Detection requires ALL of:
- Force push (old head is not ancestor of new head)
- Same number of commits with matching author + date + message
- Patch-ID equivalence between old and new diffs (content verification)
Step 3 is critical. Without it, an attacker could forge commit metadata while injecting different code.
- Gated behind the
approval_reset_rebase_detectionfeature flag - Errors are rescued and logged; the safe default (reset approvals) is used on failure
What changed
Source files (6 files)
-
lib/gitlab/gitaly_client/commit_service.rb—context_free:keyword with proto reflection for transition safety -
lib/gitlab/git/repository.rb— pass-through -
app/models/repository.rb— pass-through -
app/models/merge_request_diff.rb—set_patch_id_shausescontext_free: truewhen flag enabled -
ee/app/services/merge_requests/reset_approvals_service.rb—rebase_only_push?early return withcommits_match?anddiffs_equivalent? - Feature flags:
approval_reset_zero_context_patch_id,approval_reset_rebase_detection
Test files (7 files)
-
spec/lib/gitlab/gitaly_client/commit_service_spec.rb—context_freeparam with/without proto support -
spec/lib/gitlab/git/repository_spec.rb— pass-through -
spec/models/repository_spec.rb— pass-through -
spec/models/merge_request_diff_spec.rb— feature flag togglescontext_free -
ee/spec/services/merge_requests/reset_approvals_service_spec.rb— rebase preserves approvals; extra commits reset; forged metadata with different content resets; error handling falls through safely -
ee/spec/services/merge_requests/reset_approvals_service_integration_spec.rb— integration tests with real git operations for rebase detection and context-free patch-ID
Documentation (3 files)
-
doc/user/project/merge_requests/approvals/settings.md— user-facing section on improved rebase handling -
doc/development/smarter_approval_resets.md— developer guide with architecture, deployment order, and E2E testing plan -
doc/development/gitaly_patches/context_free_patch_id.md— Gitaly proto/implementation reference
Deployment order
- Gitaly MR (
!8535) ships proto + Go changes - Gitaly gem bump in
gitlab-org/gitlab - This Rails MR ships
- Enable Layer 1:
Feature.enable(:approval_reset_zero_context_patch_id) - Monitor approval reset rates
- Enable Layer 2:
Feature.enable(:approval_reset_rebase_detection) - Monitor error tracking for rebase detection failures
Related issues and epics
- Epic: &544 - Smarter approval resets in merge requests (open since 2018)
- #439234 - Reduce context lines used for patch-id computation
- #337888 - Don't remove approvals on rebase
- #7834 - Allow rebases without resetting approvals
- #461938 - Investigate selective code owner removals performance
- DevEx report: Approved MR blocked 4 days
- Gitaly MR:
!8535