Clone-Git Add-Git Commit 找了一个使用 Python 编写的车辆动力学仿真项目可以根据油门、制动、转向等输入计算车辆动力系统、轮胎以及车身状态。用它来找到一个现有项目中的问题分析原因修改代码验证结果然后使用 Git 保存这次修改。这是我第一次完整走通这样的 Bug 修复流程。1. Clone 项目并运行首先从 GitHub 将仓库 Clone 到本地。这里我第一次真正理解了Clone和Download ZIP的区别。Download ZIP基本只是GitHub 当前代码 ↓ 下载一份文件 ↓ 本地普通文件夹而 Clone 得到的是远程 GitHub 仓库 ↕ 本地 Git 仓库除了代码还保留了 Git 历史以及远程仓库关系。项目 Clone 完成后我用 VS Code 打开项目。目录大致为vehicle-dynamics/ │ ├─ example_data/ ├─ vehicle_dynamics/ │ ├─ modules/ │ ├─ structures/ │ └─ utils/ │ ├─ bmw_m8.yaml ├─ main.py ├─ pyproject.toml ├─ README.md └─ setup.py第一次运行python main.py出现ModuleNotFoundError: No module named yaml原因是代码虽然已经下载下来了但 Python 环境中还没有安装这个项目需要的依赖。于是执行python -m pip install -e .这里pip install负责安装 Python 包。-e表示 Editable Install可编辑安装。.表示安装当前目录中的 Python 项目。执行后项目所需要的numpy scipy matplotlib pandas PyYAML tqdm ...都被安装完成。再次python main.py项目成功运行。2. 项目成功运行但发现了一个奇怪现象正常运行以后项目根目录突然多出了一个Braking.pickle但项目本身已经有example_data/ └─ Braking.pickle也就是说运行程序之后出现了两份vehicle-dynamics/ │ ├─ example_data/ │ └─ Braking.pickle │ └─ Braking.pickle这显然值得检查。于是打开根目录的main.py。发现这里file_path example_data/Braking.pickle url https://github.com/roadview-project/vehicle-dynamics/raw/refs/heads/main/example_data/Braking.pickle urllib.request.urlretrieve(url, Braking.pickle) os.makedirs(os.path.dirname(file_path), exist_okTrue) if not os.path.exists(file_path): print(f{file_path} not found. Downloading from {url}...) urllib.request.urlretrieve(url, file_path) print(Download complete!) else: print(f{file_path} already exists. Skipping download.)问题就出在urllib.request.urlretrieve(url, Braking.pickle)这一行。它位于文件存在判断之前。因此每次运行程序时不管example_data/Braking.pickle是否已经存在Python 都会先执行urllib.request.urlretrieve(url, Braking.pickle)于是根目录就会被额外下载一份Braking.pickle下面的判断if not os.path.exists(file_path):只负责判断example_data/Braking.pickle是否存在。所以正确逻辑其实应该是检查 example_data/Braking.pickle ↓ 是否存在 / \ 是 否 ↓ ↓ 跳过 下载而原来的逻辑实际上是先下载一份到根目录 ↓ 再检查 example_data 中有没有这就造成了重复文件。3. 不直接修改 main而是创建 Bug 修复分支这次我没有直接在main分支修改。首先创建一个新的分支git switch -c fix/duplicate-braking-download这条命令可以拆开理解git switch切换分支。-cCreate新建分支。fix/duplicate-braking-download是分支名称。执行成功Switched to a new branch fix/duplicate-braking-download此时可以理解为原来的 main │ ● \ └─ fix/duplicate-braking-download目前两个分支代码还是一样的。后面我们的 Bug 修复只发生在fix/duplicate-braking-download分支。这样即使修改失败main仍然保持原样。4. 修改代码修复非常简单。删除这一行urllib.request.urlretrieve(url, Braking.pickle)修改后file_path example_data/Braking.pickle url https://github.com/roadview-project/vehicle-dynamics/raw/refs/heads/main/example_data/Braking.pickle os.makedirs(os.path.dirname(file_path), exist_okTrue) if not os.path.exists(file_path): print(f{file_path} not found. Downloading from {url}...) urllib.request.urlretrieve(url, file_path) print(Download complete!) else: print(f{file_path} already exists. Skipping download.)现在下载动作只会发生在if not os.path.exists(file_path):成立的时候。也就是说只有文件真的不存在才下载。5. 一个很容易忽略的问题修改后必须保存这次过程中我还遇到了一个很典型的问题。我明明在 VS Code 中删除了代码但是运行git statusGit 却没有提示modified: main.py执行git diff也什么都没有。后来才发现我修改了 VS Code 编辑器中的内容但是没有按Ctrl S。这时候VS Code 编辑器中的内容和硬盘上的 main.py其实不是同一个状态。Python 和 Git 读取的都是硬盘上的文件。所以修改代码 ↓ Ctrl S ↓ 保存到硬盘 ↓ Python / Git 才能看到变化保存后VS Code 左侧main.py出现M这里M Modified说明 Git 已经发现这个文件发生了修改。6. 使用 Git 确认到底修改了什么保存之后运行git status得到On branch fix/duplicate-braking-download Changes not staged for commit: modified: main.py这说明当前分支 fix/duplicate-braking-download 被修改的文件 main.py然后执行git diffGit 显示- urllib.request.urlretrieve(url, Braking.pickle)这里的-表示这行代码被删除。这一步非常有用。因为在真正提交之前可以先检查我究竟改了什么这次git diff只有一行删除说明没有误改其他代码。7. 重新运行程序验证 Bug先删除之前旧代码生成的根目录文件Braking.pickle但是保留example_data/ └─ Braking.pickle然后再次python main.py程序输出example_data/Braking.pickle already exists. Skipping download.车辆动力学仿真继续正常运行100%|████████████████████| 9001/9001运行完成后检查目录vehicle-dynamics/ │ ├─ example_data/ │ └─ Braking.pickle │ ├─ main.py ├─ bmw_m8.yaml └─ ...根目录没有再次生成Braking.pickle这说明Bug 修复成功同时没有破坏项目原本的运行逻辑。8. Git Add准备提交修改确认修复没有问题后执行git add main.pygit add并不等于保存版本。它更像是在告诉 Git下一次 Commit我准备把main.py的这些修改包含进去。再次git status可以看到Changes to be committed: modified: main.py也就是说工作区修改 ↓ git add ↓ 暂存区9. 第一次 Commit最后执行git commit -m Fix duplicate Braking.pickle downloadGit 返回[fix/duplicate-braking-download e7aca10] Fix duplicate Braking.pickle download 1 file changed, 1 deletion(-)这里包含很多重要信息。fix/duplicate-braking-download是当前分支。e7aca10是这个 Commit 的短 ID。Fix duplicate Braking.pickle download是 Commit Message。1 file changed说明修改了一个文件。1 deletion(-)说明删除了一行代码。此时 Git 历史大致变成原作者代码 │ ● main \ ● e7aca10 Fix duplicate Braking.pickle download ↑ fix/duplicate-braking-download至此一次完整的 Bug 修复完成。10. 这次真正学到的不只是删除一行代码这次 Bug 本身其实非常简单只删除了一行urllib.request.urlretrieve(url, Braking.pickle)但完整流程比这行代码本身更重要。我第一次真正走通了Clone 开源项目 ↓ 安装项目依赖 ↓ 运行项目 ↓ 发现异常现象 ↓ 阅读源码定位原因 ↓ 创建 Git Branch ↓ 修改代码 ↓ Ctrl S 保存 ↓ git status ↓ git diff ↓ 运行程序验证 ↓ git add ↓ git commit以前学习 Git 时branch commit diff status这些词很容易变成需要背诵的概念。真正修一次 Bug 以后它们的用途就清楚很多branch给修改建立一条独立的开发路线。git status现在仓库是什么状态哪些文件发生了变化。git diff具体改了哪几行。git add选择哪些修改进入下一次提交。git commit把这一次完整修改保存成一个版本。后续这次运行项目时还发现了另一个问题RuntimeWarning: invalid value encountered in scalar divide位置在vehicle_dynamics/utils/plot_function.py与轮胎滑移率计算有关。初步检查发现在车辆速度和轮速同时为 0 时部分绘图代码可能产生0 / 0不过这属于另外一个独立问题。所以我没有在这次 Commit 中一起修改。下一步准备继续按照一个问题 → 一个修复 → 一次测试 → 一个 Commit的方式处理这个 Warning。这样 Git 历史也会更加清楚。