“今天的进度”——在很多开发团队里这是一句每天都会出现的高频问题。但真正要把“今天的进度”讲清楚往往要花上半小时翻 Git 提交记录、核对任务看板、整理变更文件、再截图发到群里。进度信息分散在代码仓库、需求文档、聊天记录和本地笔记里靠人肉汇总不仅慢还容易漏。这篇文章不是讲项目管理理论而是给出一套把“今天的进度”变成可自动生成、可接口查询、可批量汇总的工程方法。我们会实现一个轻量级的进度追踪与日报生成服务它能够读取 Git 提交、解析任务状态、输出 Markdown 日报并通过 HTTP 接口对外提供服务。整个方案不依赖重量级平台可以跑在开发机、内网服务器甚至一台旧笔记本上非常适合个人开发者、小团队以及有日报交付要求的研发场景。本文会带读者完成四件事第一搭一个能够自动汇总“今天进度”的数据层第二用脚本把 Git 记录、任务标签、文件变更整理成结构化数据第三用 FastAPI 暴露进度查询接口方便后续接入内部工具或机器人第四补上批量任务、调度执行和常见排错方式。1. 核心能力速览能力项说明项目类型开发进度追踪与日报生成工具输入来源Git 提交记录、任务看板导出、本地标注文件输出格式Markdown 日报、JSON 结构化数据、HTTP 接口运行环境Python 3.9支持 Windows / macOS / Linux启动方式命令行脚本 FastAPI 服务数据存储本地 JSON / SQLite按日期归档是否支持 API支持提供 /api/progress/today 等查询接口是否支持批量任务支持可遍历仓库目录或按日期范围批量生成硬件要求无 GPU 需求普通开发机即可适合场景个人效率复盘、小团队晨会材料、交付日报留档这里的架构思路是用“约定大于配置”的方式统一进度来源开发者在提交信息里写task#123或feat: 登录模块之类的规范化文本脚本在抓取时自动归类。对于已经存在但不太规范的老仓库也能基于文件路径和提交时间做启发式统计不需要一开始就推倒重来。2. 适用场景与使用边界这个方案适合三类用户。第一类是自己同时维护多个项目的独立开发者Git 提交分布在多个仓库里想快速知道“今天到底改了什么”第二类是需要向负责人输出日报的小团队研发与其手动整理不如让脚本按仓库维度生成一份带统计数据和时间线的报告第三类是技术管理者希望从仓库层面得到客观的进度参考而不是只依赖口头汇报。它也能解决一些实际痛点提交信息不规范导致复盘困难、多个仓库之间的进度割裂、周报/月报缺少可量化的数据支撑。基于统一的提交规范脚本可以把散落的 Git 记录变成可筛选、可统计、可查询的进度档案。但要注意边界。这个工具只能反映“代码层面的进度”无法判断需求和交互是否闭环也无法替代人工评审。对于涉及敏感业务数据的仓库导出进度信息时要先做权限评估把进度结果发送到聊天机器人或外部系统时要避免把密钥、内网地址、客户信息带上。涉及第三方工具时请优先使用官方 API 并设置最小权限 Token不要使用超管账号。3. 环境准备与前置条件先梳理运行这个方案需要哪些基础环境以及为什么要这样准备。3.1 操作系统与 Python 版本建议使用 Python 3.9 及以上版本。Windows、macOS、Linux 都可以运行但 Git 命令路径在不同系统上略有差异脚本里用git -C repo_dir的方式调用避免受到当前工作目录影响。python --version git --version如果还没有安装 Git先去官网下载对应系统的安装包安装后确认命令行里能识别git命令。Windows 用户建议在 Git Bash 或 PowerShell 中执行命令避免 PATH 配置问题。3.2 依赖安装项目依赖很轻核心是 FastAPI 和 Uvicorn用于提供 HTTP 查询服务其他模块使用 Python 标准库。pip install fastapi uvicorn如果环境较乱建议先创建虚拟环境python -m venv venv source venv/bin/activate # Linux / macOS venv\Scripts\activate # Windows pip install fastapi uvicorn3.3 Git 仓库准备这个方案直接读取 Git 历史记录因此需要确保待统计的仓库已经初始化并且有提交记录。git log --since2025-01-01 00:00:00 --until2025-01-01 23:59:59 --prettyformat:%h|%an|%ad|%s --dateformat:%Y-%m-%d %H:%M:%S上面这条命令可以快速验证仓库是否能返回可解析的提交信息。实际脚本会在内部调用类似命令并把结果写入结构化文件。4. 安装部署与启动方式考虑到不同团队的使用习惯我提供了两种运行形态命令行模式和 HTTP 服务模式。4.1 目录结构建议按下面的结构组织项目progress-tracker/ ├── config.json ├── main.py ├── scraper.py ├── reporter.py ├── server.py ├── data/ │ └── 2025-01-01.json └── reports/ └── 2025-01-01.mdconfig.json保存仓库路径、需要忽略的分支和关键词规则scraper.py负责从 Git 拉取数据reporter.py把结果渲染成 Markdownserver.py启动接口服务data和reports分别存放结构化和格式化输出。4.2 配置文件{ repos: [ { name: web-frontend, path: /path/to/web-frontend, branch: main }, { name: api-service, path: /path/to/api-service, branch: master } ], ignore_branches: [dev, release], task_keywords: [task#, feat:, fix:, docs:], output_dir: ./data, report_dir: ./reports }这里要注意path必须使用本机真实目录, 如果配置文件中有多个仓库脚本会按顺序遍历。4.3 数据抓取脚本下面是一个简化的scraper.py重点展示如何读取指定日期范围内的 Git 提交并整理成进度事件。import json import subprocess import datetime from pathlib import Path def load_config(): with open(config.json, r, encodingutf-8) as f: return json.load(f) def run_git_log(repo_path, branch, start, end): cmd [ git, -C, repo_path, log, --since, start, --until, end, --prettyformat:%h|%an|%ad|%s, --dateformat:%Y-%m-%d %H:%M:%S, branch ] result subprocess.run(cmd, capture_outputTrue, textTrue, encodingutf-8) if result.returncode ! 0: raise RuntimeError(fGit 命令执行失败: {result.stderr}) lines result.stdout.strip().split(\n) events [] for line in lines: if not line: continue parts line.split(|, 3) if len(parts) ! 4: continue commit_hash, author, date, message parts events.append({ commit: commit_hash, author: author, date: date, message: message }) return events def collect_all_repos(date_str): config load_config() all_events [] for repo in config[repos]: events run_git_log( repo[path], repo[branch], f{date_str} 00:00:00, f{date_str} 23:59:59 ) for event in events: event[repo] repo[name] all_events.extend(events) return all_events if __name__ __main__: date_str datetime.date.today().isoformat() events collect_all_repos(date_str) save_path Path(data) / f{date_str}.json save_path.parent.mkdir(exist_okTrue) with open(save_path, w, encodingutf-8) as f: json.dump(events, f, ensure_asciiFalse, indent2) print(f已保存 {len(events)} 条进度事件到 {save_path})这段脚本做的事情很简单读取配置、遍历仓库、调用git log、解析输出、写入当日 JSON 文件。执行后可以用python scraper.py完成一次抓取。4.4 日报生成脚本结构化的 JSON 适合程序处理但别人看起来不直观。reporter.py可以生成 Markdown 日报import json import datetime from pathlib import Path def load_events(date_str): path Path(data) / f{date_str}.json if not path.exists(): return [] with open(path, r, encodingutf-8) as f: return json.load(f) def classify_event(message): for keyword in [task#, feat:, fix:, docs:]: if keyword in message.lower(): return keyword.replace(:, ).replace(#, ) return other def render_report(events, date_str): lines [f# 进度日报 {date_str}, ] by_repo {} for event in events: repo event.get(repo, unknown) by_repo.setdefault(repo, []).append(event) total len(events) lines.append(f今日提交总数{total}) lines.append() for repo, repo_events in by_repo.items(): lines.append(f## {repo}) lines.append() for e in repo_events: category classify_event(e[message]) lines.append(f- [{category}] {e[message]}{e[author]} {e[date]} {e[commit]}) lines.append() return \n.join(lines) if __name__ __main__: date_str datetime.date.today().isoformat() events load_events(date_str) content render_report(events, date_str) report_path Path(reports) / f{date_str}.md report_path.parent.mkdir(exist_okTrue) report_path.write_text(content, encodingutf-8) print(f已生成日报{report_path})执行python reporter.py后会在reports/目录下看到当天的 Markdown 日报结构按仓库分组每条提交带类型标签。4.5 启动 HTTP 服务如果要让其他人或机器人直接查询进度就需要启动服务。server.py提供一个最简单的读取接口import json import datetime from pathlib import Path from fastapi import FastAPI, HTTPException app FastAPI(titleProgress Tracker API) def load_events(date_str): path Path(data) / f{date_str}.json if not path.exists(): return None with open(path, r, encodingutf-8) as f: return json.load(f) app.get(/api/progress/{date}) def get_progress(date: str): events load_events(date) if events is None: raise HTTPException(status_code404, detail没有找到该日期的进度数据) return { date: date, total: len(events), events: events } app.get(/api/progress/today) def get_today_progress(): today datetime.date.today().isoformat() return get_progress(today) if __name__ __main__: import uvicorn uvicorn.run(app, host127.0.0.1, port8800)启动服务python server.py看到 Uvicorn 的启动日志后访问http://127.0.0.1:8800/api/progress/today就能查当天的进度。如果端口被占用可以修改port参数。5. 功能测试与效果验证部署只完成了一半关键是验证功能是否符合预期。下面按维度进行测试。5.1 基础抓取测试先手动在某个测试仓库中提交一条记录提交信息包含feat: 用户登录接口然后执行抓取脚本python scraper.py预期结果data/目录下生成当天的 JSON 文件其中包含至少一条事件记录message字段为feat: 用户登录接口。如果 JSON 文件是空的可以先用最简 Git 命令验证仓库路径git -C /path/to/repo log --oneline -1确认命令能正常返回提交记录后再重新执行。5.2 日报生成测试执行python reporter.py打开reports/下的 Markdown 文件确认提交按仓库分组每条记录带类型标签和作者信息。判断成功的标准是日报能直接复制到群聊或周报中不需要再手工补充关键信息。5.3 多仓库聚合测试在config.json中配置两个仓库分别在不同时间点各提交一条记录。运行python scraper.py后查看 JSON 文件中的repo字段是否与配置的仓库名称一致。如果其中一个仓库抓取失败脚本会终止因此建议在run_git_log中增加异常隔离单个仓库失败只打印警告不影响其他仓库继续执行。5.4 接口服务测试启动服务后用 curl 验证接口curl http://127.0.0.1:8800/api/progress/today预期返回 JSON内容包括date、total和events数组。如果返回 404多半是data/下还没有当日文件先执行抓取脚本再访问。再测试指定日期curl http://127.0.0.1:8800/api/progress/2025-01-01这一步可以确认日期参数解析没有问题。5.5 提交信息分类测试提交信息规范程度直接影响分类效果。建议测试以下类型的提交信息提交信息示例预期分类feat: 新增用户注册页面featfix: 修正登录接口超时问题fixdocs: 更新接口文档docstask#123 完成订单状态机taskupdate codeother如果团队有统一提交规范可以在config.json的task_keywords中直接扩展。对于老仓库里不规范的提交分类为other也没关系至少时间线和仓库归属是准确的。6. 接口 API 与批量任务接口不只是提供一个“今天查询”它还可以支撑更丰富的进度消费场景比如接入企业微信机器人、定时推送到邮件、或者在前端页面上展示。6.1 按日期范围查询如果想知道最近一周的进度可以在服务端增加一个范围查询接口from fastapi import Query app.get(/api/progress/range) def get_progress_range(start: str Query(...), end: str Query(...)): start_date datetime.date.fromisoformat(start) end_date datetime.date.fromisoformat(end) results [] current start_date while current end_date: date_str current.isoformat() events load_events(date_str) if events: results.append({ date: date_str, total: len(events), events: events }) current datetime.timedelta(days1) return { start: start, end: end, days: len(results), data: results }调用方式curl http://127.0.0.1:8800/api/progress/range?start2025-01-01end2025-01-07这个接口设计时要注意性能边界如果跨度为几个月逐日读取大量 JSON 文件会变慢后续可以改成 SQLite 存储并给date字段建索引。6.2 Python 调用示例如果希望把进度查询集成到自动化脚本中可以用 requests 调用import requests url http://127.0.0.1:8800/api/progress/today response requests.get(url, timeout10) if response.status_code 200: data response.json() print(f今日进度事件数: {data[total]}) for event in data[events]: print(f [{event[repo]}] {event[message]}) else: print(查询失败状态码:, response.status_code)6.3 批量统计多个日期在命令行模式下可以写一个批量处理脚本把多个日期的进度分别生成报告import subprocess import datetime start datetime.date(2025, 1, 1) end datetime.date(2025, 1, 7) current start while current end: date_str current.isoformat() subprocess.run([python, scraper.py], checkTrue) subprocess.run([python, reporter.py], checkTrue) current datetime.timedelta(days1)更精细的做法是在脚本中注入日期参数而不是每次使用“今天”这样可以在历史数据上批量补做报告。建议先用小日期范围测试确认输出无误后再扩大到整月。6.4 批量任务失败处理处理多个仓库或多个日期时失败是常态。建议在批量循环里增加以下机制单仓库失败不中断整个任务记录错误后继续输出日志目录独立存放方便定位对生成结果做简单校验比如 JSON 文件存在且事件数大于 0。import logging logging.basicConfig( filenameprogress.log, levellogging.INFO, format%(asctime)s %(levelname)s %(message)s ) def safe_collect(repo_path): try: # 调用抓取逻辑 pass except Exception as e: logging.error(f仓库抓取失败: {repo_path}, error: {e})这样即使有仓库因网络、权限或路径问题失败也只是在日志中留下记录其他仓库仍然可以正常产出。7. 资源占用与性能观察这套工具本质上是“Git 历史读取 文本处理”对硬件要求极低。即使同时配置 10 个中等规模的仓库单次抓取的内存占用也不会成为瓶颈。但有几个细节值得观察。7.1 单次抓取耗时影响耗时的主要因素不是仓库大小而是git log的筛选范围和仓库对象数量。第一次抓取时如果仓库较大且日期范围跨几个月Git 需要遍历的对象会多一些后续增量抓取通常很快。更稳妥的做法是先用--since限定较小范围配置一次全量统计后再按天增量运行。7.2 进程和端口占用HTTP 服务启动后注意端口占用问题。如果8800被其他程序占用Uvicorn 会启动失败可以换一个端口uvicorn server:app --host 127.0.0.1 --port 8801如果多次启动服务出现端口冲突可以用系统命令查看占用情况lsof -i:8800 # macOS / Linux netstat -ano | findstr 8800 # Windows7.3 降低 IO 压力的方式如果仓库数量特别多每个仓库都执行一次git log会有一定 IO 开销。建议在同一台机器上控制并发不要同时运行十几个git log子进程。可以使用一个简单的顺序循环或者用线程池但限制最大并发数例如 3 个。7.4 日志与磁盘清理每次抓取都会生成一份日期 JSON随着时间推移文件数量会增加。虽然单个文件不大但建议每个月归档一次旧数据把超过三个月的文件压缩为 zip 或迁移到冷存储避免目录越来越乱。8. 常见问题与排查方法下面列出从部署到使用过程中可能遇到的典型问题。问题现象可能原因排查方式解决方案python scraper.py报找不到仓库路径config.json路径错误检查配置中的path是否为绝对路径修改路径或改用相对路径并校正工作目录Git 命令执行失败本机未安装 Git 或 Git 不在 PATH执行git --version安装 Git 并重启终端日报中提交信息乱码Git 提交编码与终端不一致检查 i18n.commitEncoding在脚本中强制使用 UTF-8 解码接口返回 404当日数据文件不存在检查data/目录是否有对应日期文件先执行抓取脚本端口 8800 被占用其他进程占用该端口用 netstat/lsof 查看修改服务端口批量遍历时单个仓库失败导致全部中断未对单仓库错误做隔离在循环内捕获异常增加 try/except 并记录日志分类统计不准确提交信息没有按约定书写查看提交历史中的 message 格式在配置中增加关键词或规范后续提交查询区间过大响应慢每天读一个 JSON 文件IO 太多统计耗时并观察文件数量改用 SQLite 存储或增加缓存这里特别提醒一点如果一条提交信息包含多个任务例如feat: 登录模块fix: 超时问题脚本的分类会以第一个命中关键词为准。如果团队有更细粒度需求可以在提交规范里要求一个提交只对应一个问题。9. 最佳实践与使用建议工具本身并不复杂工程化的关键是约定和流程。下面几条建议来自实际使用经验。9.1 先定提交规范再谈自动统计自动进度统计的效果高度依赖 Git 提交质量。建议团队统一使用类似type(scope): description的格式并为每个任务关联编号。提交规范不需要很复杂能保证三个信息即可变更类型、影响模块、任务编号。这样分类脚本不需要特别复杂的算法就能输出结构清晰的日报。9.2 第一次先小范围验证不要在一开始就把几十个仓库全部加进配置。先选一个提交记录规范、更新频率正常的仓库跑通流程确认日报内容符合预期后再逐步增加其他仓库。这样排查问题时定位更简单。9.3 文件目录分离管理输入配置、脚本、中间 JSON、最终报告应该放在不同目录。config.json可以纳入版本管理但注意不要把仓库路径泄漏到外部data/和reports/建议加入.gitignore避免因为本地路径问题污染代码仓库。9.4 定时调度对每日日报场景可以配合系统调度器自动执行。Linux 环境用 cron0 19 * * * cd /path/to/progress-tracker python scraper.py python reporter.pyWindows 环境用“任务计划程序”定时执行 bat 或 PowerShell 脚本。定时任务里建议使用绝对路径并把输出重定向到日志文件cd /path/to/progress-tracker python scraper.py run.log 219.5 接口服务安全这个方案默认绑定127.0.0.1只能本机访问。如果要在内网其他机器查询改成0.0.0.0会导致端口暴露需要配合防火墙限制来源 IP。如果放到公网必须增加认证机制至少使用一个简单的 Token 校验from fastapi import Header, HTTPException API_TOKEN your-token app.get(/api/progress/today) def get_today_progress(x_token: str Header(...)): if x_token ! API_TOKEN: raise HTTPException(status_code401, detailinvalid token) return get_progress(datetime.date.today().isoformat())密码和 Token 不要直接写在代码里可以通过环境变量读取。9.6 合规与授权提醒如果统计范围包含他人提交代码或者需要把进度发送到公司群、外部平台请确认数据使用范围和权限。单个开发者的本地仓库相对简单但团队仓库中可能包含需要保密的设计文档、业务逻辑甚至客户信息。生成日报前建议人工浏览一遍确认没有敏感内容再对外发送。10. 总结与下一步这个“今天的进度”项目最值得尝试的点是把分散在 Git 仓库里的提交记录变成可查询、可汇总、可自动发布的进度数据。它不依赖复杂的项目管理平台也不需要 GPU 或重型中间件一组简单的 Python 脚本加上定时任务就能在个人电脑上跑出每日进度。建议拿到代码后先做三件事第一在单个仓库上抓取当天提交确认 JSON 输出正确第二生成一次 Markdown 日报检查分类和分组是否符合团队习惯第三启动接口服务用 curl 或 requests 验证 HTTP 查询路径。如果这三个场景都通了再考虑接入定时调度、扩展 SQLite 存储或者让日报自动推送到群聊。最容易踩的坑是 Git 路径配置和提交信息编码问题这两类问题通常会在第一次执行时暴露处理方式也相对固定。后续可以扩展的方向包括接入主流任务看板的 API、按分支合并情况统计功能交付进度、把日报数据转成周报和月报以及在前端页面上加一个简单的可视化时间线。