git完整工作流程
Git 多人协作工作流完整指南
本文档面向使用 GitHub 进行多人协作开发的团队,涵盖从仓库初始化到日常开发的完整流程。
以 Qt UI 插件组件库项目为实际背景编写。
一、仓库初始化
1.1 创建远程仓库
在 GitHub 网页上点击右上角 + → New repository,填写仓库名称,选择 Private 或 Public,建议勾选 Add a README file,然后点击 Create repository。
1.2 克隆仓库到本地
# 将远程仓库克隆到本地(HTTPS 方式)
git clone https://github.com/你的用户名/仓库名.git
# SSH 方式(推荐,免密码)
git clone git@github.com:你的用户名/仓库名.git
# 进入项目目录
cd 仓库名
1.3 本地已有项目关联远程仓库
如果本地已经有项目代码,还没有 Git 仓库:
# 初始化本地 Git 仓库
git init
# 关联远程仓库地址
git remote add origin https://github.com/你的用户名/仓库名.git
# 拉取远程仓库内容(如果远程有 README 等文件)
git pull origin main --allow-unrelated-histories
# 添加所有文件到暂存区
git add .
# 提交
git commit -m "init: 初始化项目结构"
# 推送到远程 main 分支
git push -u origin main
1.4 配置用户信息
# 设置全局用户名和邮箱(所有仓库生效)
git config --global user.name "你的名字"
git config --global user.email "你的邮箱"
# 仅对当前仓库设置(优先级高于全局)
git config user.name "你的名字"
git config user.email "你的邮箱"
# 查看当前配置
git config --list
二、分支管理
2.1 分支模型说明
推荐采用以下分支模型:
| 分支 | 用途 | 谁可以合并 | 生命周期 |
|---|---|---|---|
main |
稳定发布版本 | 仅管理员通过 PR | 永久 |
develop |
开发主线,集成所有功能 | 通过 PR 合并 | 永久 |
feature/xxx |
单个功能的开发分支 | 开发者自己维护 | 功能完成后删除 |
bugfix/xxx |
Bug 修复分支 | 开发者自己维护 | 修复完成后删除 |
release/x.x.x |
发布准备分支 | 管理员维护 | 发布后删除 |
hotfix/xxx |
紧急修复分支(从 main 拉出) | 管理员维护 | 修复后删除 |
2.2 创建 develop 分支(项目初始化时执行一次)
# 基于 main 创建 develop 分支
git checkout -b develop
# 推送 develop 到远程
git push -u origin develop
2.3 查看分支
# 查看本地分支(*号标记当前分支)
git branch
# 查看远程分支
git branch -r
# 查看所有分支(本地 + 远程)
git branch -a
# 查看每个分支的最后一次提交
git branch -v
2.4 创建功能分支
# 先切换到 develop 分支
git checkout develop
# 拉取最新的 develop 代码(重要!确保基于最新代码开发)
git pull origin develop
# 创建并切换到新的功能分支
git checkout -b feature/ipinput
# 此时你已经在 feature/ipinput 分支上,可以开始开发
分支命名规范:
feature/ipinput → 新增 IP 输入组件
feature/switchbtn → 新增开关按钮组件
bugfix/ipinput-crash → 修复 IP 输入组件崩溃问题
hotfix/theme-loading → 紧急修复主题加载失败
release/1.0.0 → 1.0.0 版本发布准备
2.5 切换分支
# 切换到已有分支
git checkout develop
# 切换到远程分支(本地没有但远程有的分支)
git fetch origin
git checkout feature/switchbtn
# Git 会自动创建本地分支并跟踪远程分支
2.6 删除分支
# 删除本地分支(必须先切换到其他分支)
git branch -d feature/ipinput
# 强制删除本地分支(未合并的分支需要强制删除)
git branch -D feature/ipinput
# 删除远程分支
git push origin --delete feature/ipinput
三、日常开发流程
3.1 查看文件状态
# 查看当前工作区状态
git status
# 简洁模式查看状态
git status -s
状态说明:
?? 文件名 → 新文件,未被 Git 追踪
M 文件名 → 已修改,已添加到暂存区
M 文件名 → 已修改,尚未添加到暂存区
A 文件名 → 新文件,已添加到暂存区
D 文件名 → 文件已删除
3.2 添加文件到暂存区
# 添加单个文件
git add src/widgets/ipinput/ipinput.h
# 添加整个目录
git add src/widgets/ipinput/
# 添加所有修改过的文件
git add .
# 添加所有 .cpp 文件
git add *.cpp
# 交互式选择要添加的内容(可以选择文件中的部分修改)
git add -p
3.3 提交
# 提交(会打开编辑器写提交信息)
git commit
# 直接写提交信息
git commit -m "feat: 完成 IpInput 组件基础框架"
# 添加并提交(仅对已追踪的文件有效,新文件无效)
git commit -am "fix: 修复 IpInput 焦点切换逻辑"
提交信息规范(推荐 Conventional Commits):
feat: 新功能
fix: Bug 修复
docs: 文档变更
style: 代码格式调整(不影响功能)
refactor: 重构(既不是新功能也不是修复)
test: 测试相关
chore: 构建工具、辅助工具相关变更
perf: 性能优化
示例:
feat: 新增 IpInput 组件,支持 IPv4 地址输入和验证
fix: 修复 SwitchButton 在高 DPI 下绘制偏移的问题
docs: 补充 IpInput 组件使用文档和接口说明
refactor: 将 IpInput 的绘制逻辑抽离到独立方法中
chore: CMakeLists 新增 widgets/ipinput 子目录
3.4 查看提交历史
# 查看提交历史
git log
# 简洁模式(一行一条)
git log --oneline
# 显示分支图
git log --oneline --graph --all
# 查看某个文件的修改历史
git log --oneline -- src/widgets/ipinput/ipinput.cpp
# 查看最近 5 条提交
git log -5 --oneline
3.5 查看差异
# 查看工作区与暂存区的差异(未 add 的修改)
git diff
# 查看暂存区与上次提交的差异(已 add 未 commit 的修改)
git diff --cached
# 查看某个文件的差异
git diff src/widgets/ipinput/ipinput.cpp
# 查看两个分支之间的差异
git diff develop..feature/ipinput
# 查看两个分支之间差异的文件列表
git diff develop..feature/ipinput --name-only
四、推送与拉取
4.1 推送到远程
# 首次推送新分支到远程(-u 建立追踪关系)
git push -u origin feature/ipinput
# 后续推送(已建立追踪关系后)
git push
# 推送指定分支
git push origin feature/ipinput
# 强制推送(rebase 之后需要,慎用!仅用于自己的 feature 分支)
git push --force-with-lease
4.2 拉取远程代码
# 拉取并合并(等于 fetch + merge)
git pull origin develop
# 仅拉取,不合并(推荐先 fetch 再决定如何合并)
git fetch origin
# fetch 之后查看远程分支的更新情况
git log origin/develop --oneline -5
# fetch 之后手动合并
git merge origin/develop
4.3 fetch 与 pull 的区别
git fetch 只是把远程的最新数据下载到本地仓库,但不会改动你的工作区。你可以先看看有什么更新,再决定是否合并。git pull 则是直接下载并合并,等于 fetch + merge 一步到位。建议在不确定远程改了什么的时候,先用 fetch 看一看。
五、合并与变基
5.1 merge(合并)
# 将 feature/ipinput 合并到 develop(本地操作)
git checkout develop
git merge feature/ipinput
# 合并时产生一个合并提交(即使可以快进合并也保留合并记录)
git merge --no-ff feature/ipinput -m "merge: 合并 feature/ipinput 到 develop"
5.2 rebase(变基)
# 在 feature 分支上执行,将自己的提交"搬"到 develop 最新提交之后
git checkout feature/ipinput
git fetch origin
git rebase origin/develop
# 如果有冲突,解决后继续
git add .
git rebase --continue
# 放弃本次 rebase
git rebase --abort
5.3 merge 与 rebase 的选择
| 场景 | 推荐方式 | 理由 |
|---|---|---|
| 将 develop 最新代码同步到 feature 分支 | rebase |
保持线性历史,提交记录干净 |
| 将 feature 分支合并到 develop | merge --no-ff |
保留合并记录,可追溯 |
| 多人共同开发同一个 feature 分支 | merge |
rebase 会改写历史,影响他人 |
核心原则:只对自己的本地分支使用 rebase,不要对已经推送到远程且有他人使用的分支执行 rebase。
5.4 解决冲突
冲突发生时,Git 会在文件中标记冲突区域:
<<<<<<< HEAD
// 当前分支的代码
void IpInput::init() {
setMaxLength(15);
}
=======
// 要合并进来的分支的代码
void IpInput::init() {
setMaxLength(15);
setPlaceholderText("0.0.0.0");
}
>>>>>>> feature/ipinput
解决步骤:
# 1. 打开冲突文件,手动编辑,保留正确的代码,删除冲突标记
# 2. 编辑完成后,将解决好的文件添加到暂存区
git add src/widgets/ipinput/ipinput.cpp
# 3a. 如果是 merge 冲突,执行提交
git commit -m "merge: 解决 ipinput 合并冲突"
# 3b. 如果是 rebase 冲突,执行继续
git rebase --continue
六、Pull Request 工作流(核心)
6.1 什么是 Pull Request
Pull Request(简称 PR)是一种代码合并请求机制。开发者不直接将代码推送到 develop 或 main 分支,而是先推送到自己的 feature 分支,然后在 GitHub 上发起一个请求,由团队成员审核代码后再执行合并。
6.2 为什么必须使用 PR
直接 push 到 develop 的做法存在以下问题:
- 没有人审核代码,质量无法保证
- 出了问题难以追溯是谁合并的、为什么合并的
- 无法利用 CI 自动检查编译和测试
- 无法在合并前讨论方案
- 多人同时操作 develop 容易互相覆盖
6.3 完整的 PR 流程(以开发 IpInput 组件为例)
第一步:创建分支并开发
# 切换到 develop,确保是最新的
git checkout develop
git pull origin develop
# 创建功能分支
git checkout -b feature/ipinput
# --- 开发代码 ---
# 查看改动
git status
git diff
# 暂存并提交(可以有多次提交)
git add .
git commit -m "feat: 实现 IpInput 组件基础框架"
# 继续开发...
git add .
git commit -m "feat: IpInput 添加输入验证和焦点切换逻辑"
# 继续开发...
git add .
git commit -m "docs: 补充 IpInput 组件使用说明"
第二步:推送到远程
# 推送功能分支到远程仓库
git push -u origin feature/ipinput
第三步:在 GitHub 上创建 Pull Request
- 打开 GitHub 仓库页面,你会看到一个黄色横幅提示"feature/ipinput had recent pushes",点击旁边的
Compare & pull request按钮 - 如果没有看到横幅,点击顶部的
Pull requests选项卡,再点击右上角绿色的New pull request按钮 - 设置合并方向:
- base:
develop(目标分支,代码要合并到哪里) - compare:
feature/ipinput(源分支,你的代码从哪来)
- base:
- 填写 PR 信息:
- Title:
feat: 新增 IpInput 组件 - Description: 描述你做了什么、为什么这样做、如何测试的
- Title:
- 在右侧面板设置:
- Reviewers: 选择审核人
- Assignees: 选择负责人(通常是自己)
- Labels: 选择标签(如
enhancement、widget) - Milestone: 如果有版本规划,选择对应的里程碑
- 点击
Create pull request
第四步:代码审核(Code Review)
审核人在 PR 页面的 Files changed 选项卡中查看代码差异,可以对某一行代码点击 + 号添加评论,审核完成后选择以下操作之一:
Approve:审核通过,可以合并Request changes:需要修改,打回给开发者Comment:仅评论,不做审核决定
第五步:根据审核意见修改(如果有)
# 在本地 feature/ipinput 分支上修改代码
git checkout feature/ipinput
# 修改代码...
# 提交修改
git add .
git commit -m "fix: 根据审核意见修复 IpInput 输入验证逻辑"
# 推送更新(PR 会自动更新,不需要重新创建 PR)
git push
第六步:合并 PR
审核通过后,在 PR 页面底部点击绿色的 Merge pull request 按钮。GitHub 提供三种合并方式:
| 方式 | 说明 | 适用场景 |
|---|---|---|
| Create a merge commit | 保留所有提交,创建一个合并提交 | 默认选择,适合大多数场景 |
| Squash and merge | 将所有提交压缩为一条,再合并 | 适合提交很琐碎的情况 |
| Rebase and merge | 将提交逐个变基到目标分支上 | 适合追求线性历史 |
推荐团队统一使用 Squash and merge,这样 develop 分支的每条提交记录对应一个完整的功能,历史清晰。
第七步:合并后清理
# 切换回 develop
git checkout develop
# 拉取合并后的最新代码
git pull origin develop
# 删除本地功能分支
git branch -d feature/ipinput
# 删除远程功能分支(GitHub 合并时可以勾选自动删除)
git push origin --delete feature/ipinput
6.4 两人并行开发的完整时间线
以 A 开发 feature/ipinput、B 开发 feature/switchbtn 为例:
时间线:
1. A 和 B 各自从 develop 创建分支
A: git checkout -b feature/ipinput
B: git checkout -b feature/switchbtn
2. A 和 B 各自开发,互不影响
3. A 先完成,推送并创建 PR
A: git push -u origin feature/ipinput
A: 在 GitHub 上创建 PR → develop
4. 团队审核 A 的 PR,审核通过后合并
此时 develop 已包含 A 的代码
5. B 完成开发,推送前先同步 develop 的最新代码
B: git fetch origin
B: git rebase origin/develop
(如果有冲突,B 在本地解决)
B: git push -u origin feature/switchbtn
6. B 在 GitHub 上创建 PR → develop
此时 PR 显示无冲突,可以直接审核合并
7. 团队审核 B 的 PR,审核通过后合并
关键点:谁后合并谁负责同步最新的 develop 并解决冲突。
七、撤销与回退
7.1 撤销工作区的修改(还没有 add)
# 撤销单个文件的修改,恢复到最后一次提交的状态
git checkout -- src/widgets/ipinput/ipinput.cpp
# 撤销所有文件的修改
git checkout -- .
7.2 撤销暂存(已经 add 但还没有 commit)
# 将文件从暂存区移出,但保留工作区的修改
git reset HEAD src/widgets/ipinput/ipinput.cpp
# 撤销所有暂存
git reset HEAD .
7.3 修改最后一次提交
# 修改最后一次提交的信息
git commit --amend -m "feat: 修正提交信息"
# 追加文件到最后一次提交(不改提交信息)
git add 忘记添加的文件.cpp
git commit --amend --no-edit
注意:如果这次提交已经推送到远程,amend 之后需要 force push,仅限自己的 feature 分支使用。
7.4 回退提交
# 回退到某个提交,保留工作区的修改(soft 模式)
git reset --soft HEAD~1
# 回退到某个提交,保留工作区但清除暂存区(mixed 模式,默认)
git reset HEAD~1
# 回退到某个提交,丢弃所有修改(hard 模式,危险!)
git reset --hard HEAD~1
# 回退到指定的提交哈希
git reset --hard abc1234
7.5 安全回退(生成一个新的"撤销"提交)
# 撤销某次提交的内容,但不改变历史(适合已推送到远程的分支)
git revert abc1234
# 撤销最近一次提交
git revert HEAD
revert 与 reset 的区别:reset 是直接删除提交历史,revert 是新建一个提交来抵消之前的改动。对于已经推送到远程的代码,应该用 revert 而不是 reset。
八、暂存工作区(stash)
当你正在 feature/ipinput 上开发到一半,突然需要切换到其他分支处理紧急问题时:
# 暂存当前所有未提交的修改
git stash
# 暂存时附带说明信息
git stash save "ipinput: 输入验证逻辑写了一半"
# 查看暂存列表
git stash list
# 现在可以安全切换分支
git checkout develop
# --- 处理完紧急问题后 ---
# 切换回原来的分支
git checkout feature/ipinput
# 恢复暂存的修改(恢复并从暂存列表中删除)
git stash pop
# 恢复暂存的修改(恢复但保留在暂存列表中)
git stash apply
# 恢复指定的暂存
git stash apply stash@{1}
# 删除某个暂存
git stash drop stash@{0}
# 清空所有暂存
git stash clear
九、标签管理(Tag)
标签用于标记版本发布点。
# 创建轻量标签
git tag v1.0.0
# 创建附注标签(推荐,包含更多信息)
git tag -a v1.0.0 -m "release: 组件库 1.0.0 版本发布"
# 给历史提交打标签
git tag -a v0.9.0 abc1234 -m "release: 0.9.0 预览版"
# 查看所有标签
git tag
# 查看标签详细信息
git show v1.0.0
# 推送单个标签到远程
git push origin v1.0.0
# 推送所有标签到远程
git push origin --tags
# 删除本地标签
git tag -d v1.0.0
# 删除远程标签
git push origin --delete v1.0.0
十、.gitignore 配置
在项目根目录创建 .gitignore 文件,告诉 Git 哪些文件不需要跟踪。
Qt/C++ 项目推荐配置:
# 编译输出
build/
cmake-build-*/
out/
*.o
*.obj
*.exe
*.dll
*.so
*.dylib
*.a
*.lib
moc_*
ui_*
qrc_*
# Qt Creator 生成文件
*.pro.user
*.pro.user.*
*.qbs.user
*.qbs.user.*
*.moc
*.qmlc
*.jsc
# IDE 配置
.idea/
.vscode/
*.swp
*.swo
*~
# 系统文件
.DS_Store
Thumbs.db
# CMake 生成文件
CMakeFiles/
CMakeCache.txt
cmake_install.cmake
Makefile
compile_commands.json
# 如果某些文件已经被追踪了,添加到 .gitignore 后需要先清除缓存
git rm -r --cached build/
git commit -m "chore: 清除 build 目录的 Git 追踪"
十一、实用技巧
11.1 查看某一行代码是谁写的
git blame src/widgets/ipinput/ipinput.cpp
11.2 搜索提交历史
# 按提交信息搜索
git log --grep="IpInput"
# 按作者搜索
git log --author="张三"
# 按时间范围搜索
git log --after="2025-01-01" --before="2025-03-01"
11.3 查看某次提交改了什么
# 查看某次提交的详细改动
git show abc1234
# 只看改了哪些文件
git show abc1234 --name-only
11.4 cherry-pick(摘取某个提交)
# 将某个分支上的某次提交应用到当前分支
git cherry-pick abc1234
适用场景:在 feature/ipinput 上修了一个通用 Bug,想把这个修复单独应用到 develop 上。
11.5 清理未追踪的文件
# 查看哪些文件会被清理(预览)
git clean -n
# 清理未追踪的文件
git clean -f
# 清理未追踪的文件和目录
git clean -fd
十二、分支保护设置(管理员操作)
在 GitHub 仓库的 Settings → Branches 中,为 main 和 develop 添加保护规则:
推荐配置:
| 选项 | 说明 | 建议 |
|---|---|---|
| Require a pull request before merging | 禁止直接 push,必须走 PR | 必须开启 |
| Require approvals | PR 必须获得审核通过才能合并 | 建议至少 1 人 |
| Require status checks to pass | CI 检查通过才能合并 | 建议开启 |
| Require branches to be up to date | 合并前必须同步最新代码 | 建议开启 |
| Do not allow bypassing the above settings | 管理员也不能绕过 | 视情况而定 |
十三、常见问题处理
13.1 推送被拒绝(远程有新提交)
# 错误信息:! [rejected] ... (non-fast-forward)
# 解决方法一:先拉取再推送
git pull --rebase origin feature/ipinput
git push
# 解决方法二:先 fetch 再 rebase
git fetch origin
git rebase origin/feature/ipinput
git push
13.2 误删分支恢复
# 查看最近的操作记录
git reflog
# 找到删除前的提交哈希,重新创建分支
git checkout -b feature/ipinput abc1234
13.3 合并错了想撤销
# 如果还没有推送,撤销合并
git reset --hard HEAD~1
# 如果已经推送,使用 revert
git revert -m 1 合并提交的哈希
13.4 commit 了不该提交的文件
# 从 Git 追踪中移除,但保留本地文件
git rm --cached 不该提交的文件.txt
git commit -m "chore: 移除误提交的文件"
十四、推荐的日常开发节奏
每天开始工作:
1. git checkout develop
2. git pull origin develop
3. git checkout feature/我的功能分支
4. git rebase develop(同步最新代码)
开发过程中:
5. 小步提交,每完成一个小功能点就 commit
6. 提交信息写清楚改了什么
开发完成时:
7. git fetch origin
8. git rebase origin/develop(最后同步一次)
9. 解决冲突(如果有)
10. git push -u origin feature/我的功能分支
11. 在 GitHub 上创建 Pull Request
12. 等待审核,根据反馈修改
13. 合并后删除 feature 分支
十五、常用命令速查表
| 操作 | 命令 |
|---|---|
| 克隆仓库 | git clone <url> |
| 查看状态 | git status |
| 添加文件 | git add <file> |
| 提交 | git commit -m "message" |
| 推送 | git push |
| 拉取 | git pull |
| 创建并切换分支 | git checkout -b <branch> |
| 切换分支 | git checkout <branch> |
| 查看分支 | git branch -a |
| 删除本地分支 | git branch -d <branch> |
| 删除远程分支 | git push origin --delete <branch> |
| 合并分支 | git merge <branch> |
| 变基 | git rebase <branch> |
| 暂存工作区 | git stash |
| 恢复暂存 | git stash pop |
| 查看日志 | git log --oneline --graph |
| 查看差异 | git diff |
| 撤销工作区修改 | git checkout -- <file> |
| 撤销暂存 | git reset HEAD <file> |
| 回退提交 | git reset --hard HEAD~1 |
| 安全回退 | git revert <commit> |
| 打标签 | git tag -a v1.0.0 -m "message" |
| 摘取提交 | git cherry-pick <commit> |

浙公网安备 33010602011771号