鸿蒙应用开发- Pocket Tool:分支管理与协作体验升级【跨平台技术在开源鸿蒙中的使用】
此前已完成仓库详情查看、基础搜索及个人仓库列表等核心功能,实现了数据的流畅加载与展示。随着用户使用场景的深入,单一的仓库浏览已无法满足协作开发需求,本次迭代重点实现分支管理核心功能,支撑多人协作场景。
鸿蒙应用开发- Pocket Tool:分支管理与协作体验升级【跨平台技术在开源鸿蒙中的使用】
一、阶段目标与实现总结
1.1 项目迭代背景
此前已完成仓库详情查看、基础搜索及个人仓库列表等核心功能,实现了数据的流畅加载与展示。随着用户使用场景的深入,单一的仓库浏览已无法满足协作开发需求,本次迭代重点实现分支管理核心功能,支撑多人协作场景。
最终实现:
"仓库详情"页面:新增分支列表入口及管理面板
"协作中心"页面:新增分支创建、切换、删除及合并请求发起功能
二、核心实现详解
2.1 Branch模型的状态与关联处理
位置: lib/models/branch.dart
factory Branch.fromJson(Map<String, dynamic> json, String repoFullName) {
// 解析分支基础信息
final name = json['name']?.toString() ?? '';
final commit = json['commit'] as Map<String, dynamic>;
final commitSha = commit['sha']?.toString() ?? '';
final commitMessage = commit['commit']['message']?.toString() ?? '无提交信息';
// 处理分支保护状态(核心协作属性)
bool isProtected = false;
if (json.containsKey('protection')) {
isProtected = json['protection']['enabled'] as bool? ?? false;
}
// 关联仓库信息(用于后续API请求)
final List<String> repoParts = repoFullName.split('/');
String owner = '';
String repoName = '';
if (repoParts.length >= 2) {
owner = repoParts.sublist(0, repoParts.length - 1).join('/');
repoName = repoParts.last;
}
// 解析提交时间
final updatedAt = _parseDateTime(commit['commit']['committer']['date']);
return Branch(
name: name,
repoOwner: owner,
repoName: repoName,
commitSha: commitSha,
commitMessage: commitMessage,
isProtected: isProtected,
updatedAt: updatedAt,
isDefault: json['default'] as bool? ?? false
);
}
// 辅助解析方法
static DateTime? _parseDateTime(String? dateStr) {
if (dateStr == null) return null;
try {
return DateTime.parse(dateStr);
} catch (_) {
return null;
}
}
代码说明:
关联关系设计:
通过仓库全名拆分获取所有者和仓库名,建立分支与仓库的强关联,为后续分支操作API提供必要参数。支持嵌套命名空间场景(如gh_mirrors/op/OpenManus)的拆分处理。
协作属性强化:
重点解析分支保护状态(isProtected),为后续删除/合并操作提供权限判断依据;默认分支标识(isDefault)用于界面突出显示,提升用户识别效率。
2.2 分支列表与操作面板实现
位置: lib/pages/repository_detail_page.dart - _BranchListState
// 分支列表构建(支持下拉刷新)
Widget _buildBranchList() {
return RefreshIndicator(
onRefresh: _fetchBranches,
child: ListView.separated(
padding: const EdgeInsets.all(16),
itemCount: _branches.length,
separatorBuilder: (context, index) => const Divider(height: 8),
itemBuilder: (context, index) {
final branch = _branches[index];
return ListTile(
leading: branch.isDefault
? const Icon(Icons.star, color: Colors.amber)
: const Icon(Icons.code_branch),
title: Text(branch.name),
subtitle: Text(
'${branch.commitSha.substring(0, 7)} · ${branch.commitMessage}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
trailing: _buildBranchActionButtons(branch),
onTap: () => _switchBranch(branch),
);
},
),
);
}
// 分支操作按钮(根据保护状态动态显示)
Widget _buildBranchActionButtons(Branch branch) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.merge),
onPressed: () => _showMergeRequestDialog(branch),
tooltip: '发起合并请求',
),
IconButton(
icon: const Icon(Icons.delete),
onPressed: branch.isProtected || branch.isDefault
? null // 保护分支/默认分支禁用删除
: () => _confirmDeleteBranch(branch),
tooltip: branch.isProtected ? '分支已保护' : '删除分支',
disabledColor: Colors.grey[300],
),
],
);
}
2.3 分支管理API服务层
位置: lib/services/api_service.dart
/// 获取仓库分支列表
static Future<List<Map<String, dynamic>>> getRepoBranches(String owner, String repo) async {
final url = Uri.parse('$baseUrl/repos/$owner/$repo/branches');
final response = await _get(url);
if (response.statusCode == 200) {
final dynamic data = json.decode(response.body);
if (data is List) {
return data.cast<Map<String, dynamic>>();
}
throw Exception('Unexpected branches list format.');
}
throw Exception('Failed to get branches: ${response.statusCode} - ${response.body}');
}
/// 创建新分支
static Future<Map<String, dynamic>> createBranch(String owner, String repo, String branchName, String baseSha) async {
final url = Uri.parse('$baseUrl/repos/$owner/$repo/branches');
final body = json.encode({
'branch': branchName,
'ref': baseSha // 基于指定提交创建分支
});
final response = await _post(url, body: body);
if (response.statusCode == 201) {
return json.decode(response.body) as Map<String, dynamic>;
}
throw Exception('Failed to create branch: ${response.statusCode} - ${response.body}');
}
/// 删除分支
static Future<void> deleteBranch(String owner, String repo, String branchName) async {
final url = Uri.parse('$baseUrl/repos/$owner/$repo/branches/$branchName');
final response = await _delete(url);
if (response.statusCode != 204) {
throw Exception('Failed to delete branch: ${response.statusCode} - ${response.body}');
}
}
代码说明:
完整生命周期覆盖:
提供分支查询、创建、删除全流程API封装,支持基于指定提交SHA创建分支,满足精准分支管理需求。
权限兼容处理:
通过HTTP状态码精准判断操作结果,针对保护分支删除等非法操作返回明确错误信息,便于前端提示。
2.4 合并请求核心功能
位置: lib/pages/merge_request_page.dart
合并请求作为分支协作的核心环节,实现以下关键功能:
-
分支选择器: 支持源分支与目标分支的联动选择,默认选中当前分支作为源分支,主分支作为目标分支
-
冲突检测: 提交前调用API预检测分支冲突,冲突时显示冲突文件列表及解决方案提示
-
请求详情: 支持填写合并标题、描述、指定审核人,关联相关Issue
-
状态跟踪: 实时展示合并请求状态(待审核、已通过、已拒绝、合并中、已合并)
三、体验优化
-
**操作反馈强化:**分支创建/删除成功后显示顶部Toast提示,3秒后自动消失
-
冲突检测结果以高亮卡片展示,提供"查看冲突文件"快捷入口
-
长时间操作(如分支创建)显示加载对话框,防止重复提交
-
**列表优化:**默认分支添加星标标识,保护分支添加盾牌图标,提升视觉识别效率
-
分支列表按更新时间倒序排列,最新操作的分支优先展示
-
支持分支搜索过滤,输入关键词实时匹配分支名称
-
**异常处理:**网络错误时显示重试按钮,点击可重新执行操作
-
权限不足操作时显示明确提示,并提供"申请权限"跳转入口
-
删除分支时增加二次确认对话框,防止误操作

四、后续优化方向
-
**分支可视化:**添加分支历史时间线,直观展示分支创建、合并、删除记录
-
实现分支网络拓扑图,展示多分支间的关联关系
-
**协作效率提升:**支持分支权限精细化管理(如指定人员可合并到主分支)
-
添加合并请求模板,规范提交内容
-
实现合并请求审核通知(站内信+推送)
-
**离线能力增强:**支持分支列表离线缓存,无网络时可查看历史分支信息
-
离线操作记录本地存储,网络恢复后自动同步
五、测试结果
| 测试场景 | 测试用例 | 测试结果 |
|---|---|---|
| 分支管理 | 创建/删除普通分支、默认分支、保护分支 | 通过(保护分支/默认分支删除已禁用) |
| 合并请求 | 无冲突合并、有冲突检测、指定审核人 | 通过(冲突检测准确,状态跟踪正常) |
| 异常场景 | 网络中断、权限不足、重复创建同名分支 | 通过(错误提示清晰,支持重试) |
| 性能测试 | 100+分支列表加载、批量删除分支 | 通过(加载耗时<1s,无卡顿) |


六、总结
本次迭代完成了分支管理全流程功能开发,核心实现了分支的创建、查询、删除及合并请求发起与跟踪,通过精细化的权限控制和直观的操作反馈,显著提升了协作开发体验。从单一的仓库浏览工具,向轻量级协作平台迈出了关键一步。
后续将重点优化分支可视化和审核流程自动化,进一步提升团队协作效率。深夜的代码终于有了成果,期待明天用户的反馈!
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)