小团队定制上游项目时,如何用 Git 工作流持续同步更新?
建议保留现有的 dev 和功能分支结构,通过普通 merge 接收 upstream/main 的更新。每次同步时,从 dev 创建临时同步分支,在该分支上处理冲突、检查定制功能,再合回 dev。对于多人共享的 dev,通常不需要为了接收上游更新而执行 rebase。
这样可以保留双方的提交历史,也便于单独审查每次上游更新。如果双方持续修改同一部分代码,任何分支策略都无法保证不发生冲突。
分支应该怎样分工?
可以继续使用以下结构:
upstream/main:上游仓库的远程跟踪分支,通过git fetch upstream更新。dev:团队定制版本的集成分支,包含已经接收的上游更新和团队自己的修改。feature/*:从dev创建,完成后合回dev。sync/*:从dev创建,专门用于接收和验证某一次上游更新。
不必先把上游更新分别合进所有功能分支。先同步 dev,需要最新基础代码的功能分支再合并 dev 即可。
一次上游同步如何操作?
以下步骤假设已配置名为 upstream 的远程仓库,工作区干净,并且本地 dev 已包含团队当前需要集成的提交。如果团队共享远程 dev,应先按团队约定同步该分支。
先检查工作区:
git status
如果有未提交的修改,先提交或妥善暂存,再执行:
git fetch upstream
git switch dev
# 示例名称;重复同步时请使用新的分支名
git branch backup/dev-before-upstream
git switch -c sync/upstream-main
git merge upstream/main
backup/dev-before-upstream 保留同步前的提交位置,方便比较或恢复,但不会备份未提交的文件。
如果合并成功且没有冲突,可以检查差异,并运行项目已有的测试或构建。如果出现冲突,先按下一步处理。
出现冲突时,怎样保留双方需要的修改?
先查看冲突文件:
git status
逐个打开文件,根据代码含义整合上游更新和定制逻辑,移除冲突标记。处理后暂存相应文件,再完成合并:
git add path/to/resolved-file
git merge --continue
将示例路径替换成实际文件路径;多个冲突文件需要分别处理并暂存。
在这次普通合并中,ours 指当前同步分支,theirs 指合入的上游一侧。直接对整个文件选择一侧,可能丢掉另一侧仍然需要的内容,因此不要把“全部接受当前版本”或“全部接受传入版本”作为常规处理方式。
如果暂时无法判断怎样合并,可以取消本次合并:
git merge --abort
完成合并后,除了运行测试,还要检查没有产生文本冲突的关键定制代码。Git 能合并文本,但无法判断程序行为是否仍然正确。
验证后怎样合回 dev?
团队使用 Pull Request 时,可以从同步分支向 dev 发起 Pull Request,检查合并结果后再合并。
如果直接在本地集成,可以执行:
git switch dev
git merge --no-ff sync/upstream-main
如果这期间 dev 又接收了其他提交,这一步仍可能产生冲突,需要解决并重新验证。--no-ff 会保留单独的合并提交,方便识别这次同步。
怎样减少后续同步的工作量?
尽量缩小定制代码与上游代码的交叉范围。如果项目已有插件、扩展点或配置机制,优先使用。必须修改上游文件时,让每次提交围绕一个明确目的组织,避免同时混入大范围格式化、重命名和功能修改。
同步频率也有影响。较小、较频繁的更新通常更容易定位问题,但需要结合上游发布节奏和团队验证成本安排。长期功能分支在需要时合并最新 dev,可以更早暴露兼容问题。
共享的 dev 优先使用 merge。个人尚未共享的功能分支可以按团队约定使用 rebase;对已经共享的分支执行 rebase 会改写提交历史,通常还需要协调后续推送和其他成员的分支。
为什么会感觉自己的修改被覆盖?
仅凭问题描述,无法确定具体原因。正常合并会根据共同祖先整合双方变更,但冲突处理时选错内容、主动替换整个文件,或者使用 git reset --hard upstream/main,都可能使定制内容从当前分支消失。没有文本冲突的合并也可能改变程序行为。
发现异常时,可以先比较同步前后的代码:
git diff backup/dev-before-upstream HEAD
如果此前执行过重置或改写历史,可以用 git reflog 查找本地记录过的旧提交位置,再从确认无误的提交创建恢复分支。reflog 不能保证找回从未提交的修改。
备注:内容仅供参考。
