21、代码版本控制:Git基础、.gitignore配置、分支管理策略

说到代码版本控制,我敢说这是每个嵌入式工程师的必修课。早些年我刚入行那会儿,项目组还在用U盘拷代码、用日期命名文件——"main_final_0315.c"、"main_final_0315_v2.c"……现在想想真是后怕。有一次我改了个驱动文件,同事也改了同一个文件,结果我俩的修改互相覆盖,查了两天bug才发现问题。从那以后,我彻底成了Git的忠实用户。

说白了,Git就是你的代码时光机。它能记录每一次修改,能让你在不同版本之间自由穿梭,还能让团队协作变得井井有条。今天我就把Git在嵌入式项目中的核心用法掰开揉碎了讲给你听。

Git基础:你只需要记住这几个命令

很多初学者一上来就被Git的复杂概念吓住了。其实你想想看,日常开发中真正高频使用的命令就那么几个。我个人的习惯是:先掌握核心工作流,再慢慢深入高级功能。

嵌入式项目通常包含源码、头文件、链接脚本、Makefile、配置文件等。我们用一个简单的STM32项目来演示:

# 初始化一个仓库
git init

# 把文件加入暂存区
git add main.c
git add driver/
git add Makefile

# 提交到本地仓库
git commit -m "feat: 添加GPIO驱动和主循环框架"

# 查看当前状态
git status

# 查看提交历史
git log --oneline --graph

这里有个小细节我要提醒你:git commit 的提交信息一定要写清楚。我在项目中见过太多"update"、"fix"、"aaa"这种毫无意义的提交信息。等过两个月回来看,根本不知道当时改了啥。我建议你参考 Conventional Commits 规范,用 feat:fix:docs: 这样的前缀来分类。

核心工作流总结

工作区 → git add → 暂存区 → git commit → 本地仓库 → git push → 远程仓库

反过来:远程仓库 → git pull → 本地仓库 → git checkout → 工作区

.gitignore配置:别把垃圾文件提交上去

嵌入式项目有个特点:编译产物特别多。.o 文件、.hex 文件、.map 文件、IDE的工程配置……这些东西如果都提交到仓库里,那仓库体积会迅速膨胀,而且每次编译都会产生变更,非常烦人。

我曾经接手过一个项目,仓库里塞满了各种编译中间文件,光是 .o 文件就有几百兆。clone一次要等半天,而且每次pull都会因为 .o 文件冲突而报错。后来我花了一下午清理仓库,并配置了 .gitignore,世界清净了。

下面是一个嵌入式C项目的标准 .gitignore 模板:

# 编译产物
*.o
*.a
*.so
*.elf
*.hex
*.bin
*.map

# IDE配置(不同IDE不同)
.idea/
.vscode/
*.uvoptx
*.uvguix
*.uvprojx

# 构建目录
build/
Debug/
Release/

# 系统文件
.DS_Store
Thumbs.db

# 日志和临时文件
*.log
*.tmp
*.swp

小技巧:如果你不确定某个文件该不该忽略,可以先 git add 试试。如果Git提示"文件已被忽略",那就说明 .gitignore 生效了。另外,.gitignore 本身也要提交到仓库里,这样团队所有人都能共享相同的忽略规则。

分支管理策略:让团队协作不再打架

分支管理是Git的精髓所在。我见过不少团队,所有人都在 master 分支上开发,结果就是天天冲突、天天合并,代码质量一塌糊涂。其实只要用对分支策略,这些问题都能避免。

我个人最推荐的是 Git Flow 的简化版,特别适合嵌入式项目。为什么?因为嵌入式项目通常有明确的硬件版本、固件版本,需要同时维护多个release分支。

下面这张图展示了嵌入式项目常用的分支结构:

嵌入式项目分支管理策略(简化Git Flow) master develop feature/* release/* v1.0.0 发布 日常开发集成 新功能开发 发布前测试/修复 合并到develop 合并到master和develop 合并到master 箭头方向表示合并方向,虚线表示临时分支

具体怎么用呢?我拿一个实际项目举例:

# 1. 从develop分支创建feature分支
git checkout develop
git checkout -b feature/add-i2c-driver

# 2. 在feature分支上开发
# ... 写代码、测试 ...
git add .
git commit -m "feat: 添加I2C驱动基础框架"

# 3. 开发完成,合并回develop
git checkout develop
git merge --no-ff feature/add-i2c-driver

# 4. 准备发布时,创建release分支
git checkout -b release/v1.2.0

# 5. 在release分支上只做bug修复,不添加新功能
# ... 修复bug ...
git commit -m "fix: 修复I2C时钟配置错误"

# 6. 发布后合并到master和develop
git checkout master
git merge --no-ff release/v1.2.0
git tag v1.2.0

git checkout develop
git merge --no-ff release/v1.2.0

注意:千万不要在 master 分支上直接开发。master只用来存放已经发布的、稳定的版本。每次合并到master时,都应该打一个tag,比如 v1.0.0v2.1.3。这样万一出了问题,可以快速回退到上一个稳定版本。

嵌入式项目的特殊考量

嵌入式开发和纯软件项目不太一样。我遇到过几个坑,这里一并告诉你:

  • 硬件依赖问题:不同硬件版本的代码可能不兼容。建议为每个硬件版本创建独立的分支,比如 hw-v2.0hw-v3.0。通用代码放在 develop 分支,硬件相关代码放在对应分支。
  • 二进制文件管理:编译生成的 .hex.bin 文件不要提交到Git仓库。这些文件体积大、变更频繁,而且无法diff。应该用CI/CD工具自动构建和存档。
  • 子模块(submodule)的使用:如果项目依赖第三方库(比如FreeRTOS、LwIP),建议用Git submodule来管理。这样能保持依赖版本可控,不会因为第三方库更新而意外破坏你的代码。
# 添加子模块
git submodule add https://github.com/FreeRTOS/FreeRTOS.git libs/FreeRTOS

# 克隆包含子模块的项目
git clone --recursive https://github.com/your/project.git

# 更新子模块
git submodule update --remote

我的习惯:每次提交前,我都会先 git diff 看一下改了什么。这能帮我发现一些无意的修改,比如不小心改了个空格、多了个空行。保持提交的"原子性"——一个提交只做一件事,这样回滚和代码审查都会轻松很多。

好了,Git的基础用法、.gitignore 配置和分支管理策略就讲到这里。这些内容看起来简单,但真正用好需要在实际项目中不断练习。记住一点:版本控制不是束缚你的工具,而是保护你的铠甲。养成好习惯,你的代码会感谢你的。


公众号:蓝海资料掘金营,微信deep3321