You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Git Flow场景下使用git merge --squash是否会引发合并冲突?

Hey,我来帮你拆解这个Git Flow合并冲突的问题,结合你的测试场景一步步说清楚:

问题分析与解答

1. 你的提交同步方式是否存在错误?

答案是肯定的,你的同步流程踩了Git Flow的一个常见坑。在Git Flow中,当release分支合并完feature之后,正确同步到develop的方式是直接将release分支merge到develop,而不是通过cherry-pick单个commit再合并。

为什么你的方式有问题?因为cherry-pick会生成一个全新的提交(和release里的提交哈希完全不同),Git无法识别这两个提交是同一套变更。后续你再合并release到develop时,Git会把release里的变更当成“新的未合并内容”来处理,虽然这次测试中普通merge因为是追加内容没冲突,但如果是修改同一行代码,必然会触发冲突。

正确的同步流程应该是:

  • 在release分支合并feature后,直接创建PR将release合并到develop,保留完整的合并历史。这样Git能清晰跟踪哪些变更已经同步,后续合并时不会重复处理。

2. git merge --squash是否不推荐在Git Flow中使用?

绝对不推荐,Git Flow的核心就是依赖清晰的分支历史和合并轨迹来管理版本,而--squash完全违背了这个设计:

  • 它会把待合并分支的所有提交压缩成一个新的普通提交,丢失了单个功能的开发历史,后续排查问题或回滚时根本找不到具体的提交点。
  • 它不会创建合并提交,破坏了Git的分支拓扑关系,Git无法跟踪哪些变更已经被合并,后续再合并同一分支时会重复处理变更,极易引发冲突。
  • Git Flow中release、develop、feature分支的合并都需要保留明确的合并记录,--squash会让历史变得混乱,完全不符合Git Flow的规范。

3. Git如何识别待合并的提交?为何修改提交信息会影响冲突结果?

Git识别待合并提交的逻辑

Git是通过提交历史的拓扑关系来识别的:它会先找到两个分支的最近共同祖先(LCA),然后合并这个共同祖先之后,两个分支上所有提交的变更。普通的git merge会创建一个合并提交,记录两个分支的合并点,这样Git后续能明确知道哪些提交已经被合并过,不会重复处理。

提交信息影响冲突的本质

其实提交信息本身不会直接导致冲突,问题出在git merge --squash的特性上:

  • --squash不会创建合并提交,它只是把待合并分支相对于共同祖先的所有变更,当成一个“补丁”应用到当前分支,然后生成一个新的普通提交。这意味着Git无法通过历史记录识别这些变更已经被cherry-pick到develop了。
  • 你的测试中,git merge --squash && git commit --no-edit没冲突是侥幸:因为变更都是追加到文件末尾,Git在应用补丁时自动跳过了重复内容,但这种情况不稳定,换个变更方式就会冲突。
  • 而当你自定义提交信息时,Git在处理补丁的过程中,严格对比了内容,发现待合并的变更(添加4,5,6)已经存在于当前分支,所以触发了冲突。本质上是--squash不跟踪合并历史导致的重复处理。
模拟BitBucket PR合并的Shell脚本
#! /bin/bash
set -euo pipefail
IFS=$'\n\t'
function log_step() {
 echo -e "\n\e[96m${*}\e[0m"
}
# Delegates the call to the merge strategy and passing args
# You can uncomment the desired strategy
function merge() {
 # merge_simple $*
 merge_squash_rename $*
 # merge_squash_no_edit $*
}
function merge_squash_rename(){
 echo -e "\e[94mMerge ${1} into ${2}\e[0m"
 git checkout ${2}
 git merge --squash ${1}
 git commit -m "Merge ${1} into ${2}"
}
function merge_squash_no_edit(){
 echo -e "\e[94mMerge ${1} into ${2}\e[0m"
 git checkout ${2}
 git merge --squash ${1}
 git commit --no-edit
}
function merge_simple(){
 echo -e "\e[94mMerge ${1} into ${2}\e[0m"
 git checkout ${2}
 git merge ${1}
}
tmp_dir=$(mktemp -d -t strategit-XXXXXXXX)
echo "Created directory ${tmp_dir}"
cd ${tmp_dir}
# Initial state : a file named hello.txt, containing "1,2,3"
log_step "Create initial file with 1,2,3"
git init
echo '1,2,3' >> hello.txt
git add hello.txt
git commit -m 'First commit'
# Create initial branches release + develop
log_step "Create branches release and develop"
git checkout -b release
git checkout -b develop
# Work on ticket FW-456 on release
log_step "Create PR on release in order to add the line 4,5,6"
git checkout release
git checkout -b feature/FW-456
echo '4,5,6' >> hello.txt
git add hello.txt
git commit -m 'feature/FW-456'
# Keep it for cherry-pick later
ORIGINAL_COMMIT_HASH=$(git rev-parse HEAD)
log_step "Merge the PR on release"
merge feature/FW-456 release
# report PR on develop by creating a report branch
log_step "Create the report branch from develop"
git checkout develop
git checkout -b report/feature/FW-456
git cherry-pick ${ORIGINAL_COMMIT_HASH}
log_step "Merge the report branch on develop"
merge report/feature/FW-456 develop
# Work on ticket FW-789 on develop
log_step "Create a new PR from develop to add the line 7,8,9"
git checkout develop
git checkout -b feature/FW-789
echo '7,8,9' >> hello.txt
git add hello.txt
git commit -m 'feature/FW-789'
log_step "Merge the PR in develop"
merge feature/FW-789 develop
# Now, report release into develop
log_step "Create a PR on develop to report release in develop"
git checkout develop
git checkout -b chore/report-release-into-develop
# "|| true" to prevent script from exiting because of the potential conflict error
log_step "Try to merge"
git merge release || true
echo ""
echo "***************************************************"
echo "* *"
echo -e "* See result in directory \e[92m${tmp_dir}\e[0m *"
echo "* *"
echo "***************************************************"

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

火山引擎 最新活动