You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GitLab CI中如何生成仅含当前MR变更的changes.diff文件?

How to Fix GitLab CI MR Script That Includes Unrelated Merge Changes in changes.diff

Problem Context

在GitLab CI的合并请求(MR)流程中,我需要生成changes.diff文件来汇总当前MR引入的变更,用于通过clang-format-diff验证代码格式是否符合.clang-format规范,同时检查是否新增#if 0代码块。目前项目暂无法整体格式化清理,因此需避免新增不符合规范的内容。现有脚本如下:

oldrev=$(git rev-list HEAD --grep="Merge branch '.*' into 'master'" |head -n1)
newrev=${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA:-$(git rev-parse HEAD)}
# 1 查找变更文件,仅考虑新增、复制、修改(ACM),排除删除、移动
# 2 忽略非代码文件
files=$(git diff-tree --no-commit-id --name-only --diff-filter=ACM -r $oldrev $newrev | egrep "\.(c|cpp|cxx|cc|h|hpp|ipp|tpp)$")
if [[ -z "${files}" ]]; then
    echo "nothing interesting changed"
    exit 0
fi
echo "Changed files:"
echo $files
echo $files | xargs git diff -U0 --no-color $oldrev $newrev -- > changes.diff

该脚本仅在分支直接从master分叉且未合并过master的场景下正常工作,若分支存在从master合并的操作,changes.diff会包含其他MR的变更,请问如何优化该脚本?

Solution

Great question! The core issue here is that your current way of finding oldrev hunts for merge commits into master, which breaks as soon as you merge master into your branch to keep it up-to-date—this pulls in all the changes from those upstream merges, which aren't part of your MR.

Instead, we should use Git's merge-base command, which reliably finds the latest common ancestor between your MR's source branch and the target branch. This ensures we only diff the changes that are unique to your MR, regardless of how many times you've merged upstream.

Here's the optimized script:

# Use GitLab CI's target branch variable, default to master for local testing
TARGET_BRANCH=${CI_MERGE_REQUEST_TARGET_BRANCH_NAME:-master}

# Fetch the latest target branch (critical for shallow clones in CI runners)
git fetch origin $TARGET_BRANCH --depth 1

# Find the common ancestor between our branch and the target branch
oldrev=$(git merge-base origin/$TARGET_BRANCH ${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA:-$(git rev-parse HEAD)})
newrev=${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA:-$(git rev-parse HEAD)}

# 1 查找变更文件,仅考虑新增、复制、修改(ACM),排除删除、移动
# 2 忽略非代码文件
files=$(git diff-tree --no-commit-id --name-only --diff-filter=ACM -r $oldrev $newrev | egrep "\.(c|cpp|cxx|cc|h|hpp|ipp|tpp)$")

if [[ -z "${files}" ]]; then
    echo "nothing interesting changed"
    exit 0
fi

echo "Changed files:"
echo $files
echo $files | xargs git diff -U0 --no-color $oldrev $newrev -- > changes.diff

What Changed & Why:

  • git merge-base instead of merge commit hunting: This command always finds the last commit that both your branch and the target branch share. Even if you've merged master into your branch multiple times, this will correctly isolate the changes your MR introduces.
  • Target branch flexibility: We use CI_MERGE_REQUEST_TARGET_BRANCH_NAME to get the actual target branch of the MR (no more hardcoding master), which works if your project uses main or another default branch.
  • Fetch the target branch: CI runners often use shallow clones to save time, so adding git fetch origin $TARGET_BRANCH ensures we have the latest version of the target branch to compute the correct ancestor.
  • Local testing support: The script still works if you run it on your local machine, falling back to master as the target branch.

Quick Notes:

  • If your CI runner uses a very shallow clone, the --depth 1 flag on git fetch is enough to get the latest target branch commit needed for merge-base.
  • This approach completely excludes changes from upstream merges—only your MR's unique changes will be in changes.diff, which is exactly what you need for clang-format-diff validation.

内容的提问来源于stack exchange,提问作者MateuszL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 17:07:48