Blog
初学者指南:用 Git 分支和 Worktree 并行开发前后端功能
以两个前后端需求为例,讲清如何用 Git 分支和 Worktree 隔离并行开发、约定接口、处理冲突、安全合并与清理工作目录。
初学者指南:用 Git 分支和 Worktree 并行开发前后端功能
当一个项目同时需要修改多个功能时,最容易遇到的问题不是代码不会写,而是不同任务互相覆盖、改乱,最后不知道哪些修改属于哪个功能。
本文用 Lumi 用量监控项目中的两个需求作为例子,说明如何使用 Git 分支和 Worktree,让两个功能并行开发,最后安全合并。
一、这次要开发什么
假设当前要同时实现两个功能:
功能 1:用户具体服务消耗历史检索
后端和前端需要一起调整:
- 使用只读 SQLite 连接。
- 使用参数化 SQL,避免把用户输入直接拼接到 SQL 字符串中。
- 按“用户 + 服务 + 场景 + 子场景 + 资源”聚合后再分页。
- 不能先查询并截取前 50 条,再在内存中分页。
- 使用北京时间自然日作为时间筛选口径。
- 支持用户筛选、服务筛选和关键词筛选。
- 支持点击查询,也可以使用防抖查询。
- 显示真实查询区间、总结果数和当前分页。
- 当数据覆盖不完整时显示明确警告,不能把部分数据伪装成完整账单。
- 固定的 7 日分析和比较指标继续保留。
- “用户具体服务消耗”改成独立的历史检索区域,默认查询最近 7 日,但允许修改时间范围。
功能 2:Lumi 业务图表驾驶舱
这个功能单独负责:
- 展示最近 30 天数据。
- 增加业务趋势图表。
- 增加适合管理和分析的指标卡、排行榜或对比图表。
- 使用稳定的数据接口,不直接依赖页面内部的临时状态。
二、分支和 Worktree 分别解决什么问题
可以把它们理解成两层隔离:
| 概念 | 解决的问题 | 初学者理解 |
|---|---|---|
| 分支 branch | 代码版本如何保存和合并 | 一条独立的修改历史 |
| Worktree | 本地同时打开多个工作目录 | 同一个项目的多个文件夹 |
如果只创建两个分支,你仍然可以并行开发,但通常需要频繁切换目录:
flowchart LR
editOne["修改功能 1"] --> switchTwo["git switch 功能 2"]
switchTwo --> editTwo["修改功能 2"]
editTwo --> switchOne["git switch 功能 1"]
如果使用两个 Worktree,就可以同时打开两个目录:
项目根目录/
├── .worktrees/usage-history/ # 功能 1
└── .worktrees/business-dashboard/ # 功能 2
每个目录对应一个分支,因此最适合并行开发。
三、推荐的分支结构
建议先准备一个集成分支 develop,两个功能从同一个起点创建:
main
└── develop
├── feature/usage-history-query
└── feature/business-dashboard-30d
含义如下:
main:已经验证,可以发布的版本。develop:用于合并和集成多个功能。feature/usage-history-query:功能 1 的开发分支。feature/business-dashboard-30d:功能 2 的开发分支。
如果项目目前没有 develop 分支,可以先使用 main 作为基础分支。初学者不必一开始就建立复杂的分支体系。
四、开始前先检查当前状态
在项目根目录打开 PowerShell,运行:
git status
git branch --show-current
git worktree list
确认三件事:
- 当前没有未保存的重要修改。
- 你知道当前所在分支。
- 没有已经存在的同名 Worktree。
如果 git status 显示有你正在进行的修改,不要直接创建分支覆盖它。可以先提交,或者先使用临时保存:
git add .
git commit -m "保存当前工作进度"
五、创建两个 Worktree
先确认项目有 develop 分支。如果没有,可以创建:
git switch -c develop
然后在项目根目录运行:
git worktree add .worktrees/usage-history -b feature/usage-history-query develop
git worktree add .worktrees/business-dashboard -b feature/business-dashboard-30d develop
命令完成后可以检查:
git worktree list
预期会看到类似结果:
项目根目录 develop
项目根目录\.worktrees\usage-history feature/usage-history-query
项目根目录\.worktrees\business-dashboard feature/business-dashboard-30d
如果项目的 .gitignore 还没有忽略 .worktrees,请加入:
.worktrees/
Worktree 是本地工作目录,不应该被当成项目源码再次提交。
六、在两个目录中分别开发
6.1 功能 1:历史检索
进入功能 1 的目录:
cd .worktrees/usage-history
建议按以下顺序实现:
第一步:先定义接口返回结构
不要先写页面,再临时决定后端返回什么。可以先确定类似下面的结构:
{
"queryStart": "2026-07-01",
"queryEnd": "2026-07-07",
"timezone": "Asia/Shanghai",
"total": 128,
"page": 1,
"pageSize": 20,
"coverage": {
"complete": true,
"availableStart": "2026-06-01",
"availableEnd": "2026-07-24",
"message": null
},
"items": []
}
这里的 coverage.complete 很重要。查询区间没有完整数据时,前端必须显示警告,而不是只显示一个看起来很准确的金额。
第二步:实现安全查询
查询条件使用参数化 SQL。示例:
sql = """
SELECT user_name, service_name, scene, sub_scene, resource,
SUM(amount) AS total_amount
FROM usage_daily
WHERE usage_date >= ?
AND usage_date < ?
AND (? IS NULL OR user_name = ?)
GROUP BY user_name, service_name, scene, sub_scene, resource
ORDER BY total_amount DESC
LIMIT ? OFFSET ?
"""
params = [start_date, end_date_exclusive,
user_name, user_name,
page_size, offset]
不要这样写:
# 不推荐:把输入直接拼接进 SQL
sql = f"SELECT * FROM usage_daily WHERE user_name = '{user_name}'"
LIMIT、OFFSET 和筛选条件都应该经过类型校验。分页必须发生在聚合之后:
flowchart LR
records["原始记录"] --> filter["条件筛选"]
filter --> group["GROUP BY 聚合"]
group --> count["COUNT 总数"]
count --> page["LIMIT / OFFSET 分页"]
不能使用下面这种不完整方式:
flowchart LR
records["原始记录"] --> limit["先取前 50 条"]
limit --> aggregate["内存聚合"]
aggregate --> page["分页"]
因为这样会漏掉第 51 条之后的数据,结果不能代表真实账单。
第三步:处理北京时间自然日
第一版只选择日期即可,例如:
开始日期:2026-07-01
结束日期:2026-07-07
后端应把它转换为半开区间:
[2026-07-01 00:00:00, 2026-07-08 00:00:00)
也就是包含 7 月 1 日,不包含 7 月 8 日。这样可以完整包含 7 月 7 日全天,并避免 23:59:59 带来的边界问题。
第四步:实现前端查询区
前端至少应包含:
- 开始日期和结束日期。
- 用户筛选。
- 服务或关键词筛选。
- 查询按钮。
- 总结果数。
- 当前页码和总页数。
- 实际查询区间。
- 数据覆盖警告。
默认时间范围为最近 7 个北京时间自然日,但用户可以修改范围。
6.2 功能 2:30 天驾驶舱
进入功能 2 的目录:
cd .worktrees/business-dashboard
建议按以下顺序实现:
- 明确 30 天的开始日期和结束日期。
- 确认图表所需的接口字段。
- 先完成总量、趋势、服务分布等基础指标。
- 再增加排行榜和同比或环比指标。
- 对空数据、数据覆盖不全和接口失败做单独展示。
驾驶舱的 30 天统计不要复用“用户具体服务消耗”的分页接口。分页接口适合明细检索,驾驶舱更适合返回按日期、服务或用户聚合后的图表数据。
七、如何提交每个功能
功能 1 完成后:
cd .worktrees/usage-history
git status
git add .
git commit -m "增加可筛选的用户服务历史检索"
功能 2 完成后:
cd .worktrees/business-dashboard
git status
git add .
git commit -m "增加 Lumi 30 天业务图表驾驶舱"
建议一个提交只表达一个完整的逻辑修改,不要把两个功能混在同一个提交里。
八、如何合并两个功能
先回到项目根目录:
cd ../..
切换到集成分支:
git switch develop
先合并功能 1:
git merge --no-ff feature/usage-history-query
再合并功能 2:
git merge --no-ff feature/business-dashboard-30d
合并后运行完整测试,并手动检查:
- 历史检索是否真的支持任意日期范围。
- 查询是否使用北京时间自然日。
- 总数是否来自完整聚合结果。
- 分页是否发生在聚合之后。
- 覆盖不完整时是否显示警告。
- 驾驶舱是否显示最近 30 天数据。
- 两个功能是否能同时打开和使用。
九、遇到合并冲突怎么办
先查看冲突状态:
git status
Git 会在冲突文件中标记:
<<<<<<< HEAD
develop 分支的内容
=======
功能分支的内容
>>>>>>> feature/business-dashboard-30d
手动保留正确内容,删除这些标记,然后运行:
git add 冲突文件路径
git commit
如果发现合并结果不对,可以在提交前取消这次合并:
git merge --abort
初学者最常见的冲突文件是路由、公共组件、全局样式、接口类型和 package.json。两个功能开始前,最好先约定谁负责修改这些共享文件。
十、什么时候应该拆得更细
当前方案是两个功能各一个分支,已经适合大多数初学者。如果功能 1 的前端和后端也要交给两个人并行开发,可以进一步拆成:
develop
├── feature/usage-history-api
├── feature/usage-history-ui
└── feature/business-dashboard-30d
不过这时必须先写好 API 契约。否则前端和后端会同时修改同一个接口,合并时反而更复杂。
推荐顺序是:
- 先定义接口字段和时间口径。
- 后端实现接口并提供示例响应。
- 前端使用示例响应开发页面。
- 后端和前端分别测试。
- 最后在集成分支联调。
十一、开发完成后删除 Worktree
确认分支已经合并,并且不再需要本地目录后,可以删除 Worktree:
git worktree remove .worktrees/usage-history
git worktree remove .worktrees/business-dashboard
删除 Worktree 不会删除已经提交的分支。查看剩余分支:
git branch
如果以后确认分支也不再需要,再删除分支:
git branch -d feature/usage-history-query
git branch -d feature/business-dashboard-30d
十二、最终推荐方案
对于本次需求,最适合初学者的方案是:
功能 1:feature/usage-history-query + .worktrees/usage-history
功能 2:feature/business-dashboard-30d + .worktrees/business-dashboard
合并目标:develop
发布目标:main
时间口径:北京时间自然日
记住三条原则:
- 一个功能一个分支。
- 需要真正并行时,一个分支一个 Worktree。
- 合并前先测试,合并后再做一次完整联调。