Linux开发工具详解(二):Git版本控制、GitHub协作与GDB调试实战

文章目录
- Linux开发工具详解(二):Git版本控制、GitHub协作与GDB调试实战
- 一、什么是版本控制?
- 二、Git和GitHub不是同一个东西
- 三、安装Git
- 四、第一次使用Git要配置身份
- 五、Git最重要的四个区域
- 六、Git数据流一定要理解
- 七、git add到底在干什么?
- 八、git commit是什么?
- 九、commit message应该怎么写?
- 十、git push是什么?
- 十一、2026年使用GitHub要注意认证方式
- 十二、git clone:把远程仓库复制到本地
- 十三、本地已有项目如何接入Git?
- 十四、git status:最应该频繁使用的命令
- 十五、git diff:提交前看看自己改了什么
- 十六、git log:查看历史
- 十七、git pull是什么?
- 十八、.gitignore有什么用?
- 十九、一个C/C++项目的.gitignore示例
- 二十、为什么Git要有暂存区?
- 二十一、Git分支的基本思想
- 二十二、从Git进入GDB:版本管理解决不了Bug本身
- 二十三、GDB是什么?
- 二十四、调试之前为什么要加-g?
- 二十五、启动GDB
- 二十六、list:查看源代码
- 二十七、break:设置断点
- 二十八、查看和管理断点
- 二十九、run与continue
- 三十、next与step:调试最常用的区别
- 三十一、finish:把当前函数执行完
- 三十二、until:快速执行到后面
- 三十三、print:查看表达式
- 三十四、display:每次暂停自动显示
- 三十五、set var:临时修改程序变量
- 三十六、backtrace:查看调用栈
- 三十七、info locals:查看当前局部变量
- 三十八、watch:变量什么时候被改坏了?
- 三十九、条件断点
- 四十、给已有断点添加条件
- 四十一、GDB最常用命令速查
- 四十二、一个完整GDB调试案例
- 四十三、GDB调试思路比命令更重要
- 四十四、什么是CGDB?
- 四十五、Git和GDB如何形成完整工作流?
- 四十六、一套推荐的日常Git工作流程
- 四十七、Git与GDB最容易混淆的几个问题
- 四十八、整套Linux开发工具知识图
前言:写完代码以后还有两个问题
1. 代码怎么管理?
一个项目开发过程中会不断出现:
第一次能运行
第二次加功能
第三次改接口
第四次修Bug
第五次重构
如果使用最原始的方法:
project-v1
project-v2
project-v3
project-final
project-final2
project-真最终版
很快就会遇到:
哪个版本改了什么?
什么时候改的?
谁改的?
还能不能回去?
两个人怎么同时开发?
因此需要:
版本控制系统
2. Bug怎么定位?
程序运行结果错误:
Expected: 5050
Actual: 0
只靠:
printf("here1\n");
printf("here2\n");
printf("x=%d\n", x);
当然也能调试。
但复杂程序中,我们更希望:
让程序停在指定位置
单步执行
进入函数
查看变量
修改变量
查看调用栈
观察变量何时变化
这就是:
GDB
解决的问题。
所以这一篇的主线就是:
Git
↓
管理代码历史
GDB
↓
定位代码问题
一、什么是版本控制?
1. 版本控制解决什么?
版本控制系统可以记录:
谁
在什么时候
修改了哪些文件
具体改了什么
为什么修改
并允许:
查看历史
回退版本
创建分支
合并代码
多人协作
2. Git是什么?
Git 是一个:
分布式版本控制系统
“分布式”非常重要。
传统集中式系统更多依赖:
中央服务器
而 Git 每个完整克隆通常都拥有:
完整版本历史
所以本地也可以进行大量操作:
commit
log
diff
branch
并不要求时刻连接 GitHub。
二、Git和GitHub不是同一个东西
1. Git
Git 是:
版本控制工具
安装在本地。
2. GitHub
GitHub 是:
Git 代码托管与协作平台
除此之外还提供:
Issue
Pull Request
Actions
Release
Code Review
等能力。
所以:
Git != GitHub
关系更接近:
Git
↓
版本控制协议与工具
GitHub
↓
基于Git的远程托管与协作平台
三、安装Git
1. Ubuntu
sudo apt update
sudo apt install -y git
2. Fedora / RHEL
sudo dnf install -y git
旧 CentOS:
sudo yum install -y git
检查:
git --version
四、第一次使用Git要配置身份
1. 配置用户名
git config \
--global \
user.name \
"Your Name"
2. 配置邮箱
git config \
--global \
user.email \
"[email protected]"
查看:
git config --global --list
这些信息会进入:
Commit Metadata
用于标识:
谁进行了这次提交
五、Git最重要的四个区域
1. 工作区
就是你真实看到和编辑的文件:
main.c
Makefile
README.md
2. 暂存区
也叫:
Index
Staging Area
作用是:
准备“下一次 commit 到底包含哪些内容”
3. 本地仓库
执行:
git commit
之后,提交记录进入:
Local Repository
4. 远程仓库
例如:
GitHub
对应常见远端名字:
origin
六、Git数据流一定要理解
Working Tree
|
| git add
v
Staging Area
|
| git commit
v
Local Repository
|
| git push
v
Remote Repository
这张图几乎可以解释:
80%的Git入门问题
七、git add到底在干什么?
1. 它不是“提交代码”
例如:
git add main.c
只是:
把当前 main.c 的内容
加入暂存区
准备参加:
下一次 commit
2. 添加所有变化
git add .
初学阶段非常常见。
但在实际项目中,提交前建议先:
git status
确认到底有哪些文件会被提交。
八、git commit是什么?
git commit \
-m "fix: correct progress calculation"
意思是:
把当前暂存区的状态
记录为一个新的本地版本
所以:
commit
本质更接近:
创建项目历史快照
九、commit message应该怎么写?
1. 不推荐
update
修改代码
123
test
aaa
过几个月以后几乎无法理解。
2. 更推荐
例如:
fix: prevent division by zero
feat: add progress bar
refactor: split process module
docs: update README
让别人看到历史时能够快速知道:
为什么改
十、git push是什么?
执行:
git push
意味着:
把本地提交
同步到远程仓库
第一次绑定上游分支时可能使用:
git push -u origin main
以后:
git push
即可。
十一、2026年使用GitHub要注意认证方式
1. 不要再使用GitHub账户密码进行Git认证
GitHub 已经不再支持:
用户名 + GitHub登录密码
完成 Git 的 HTTPS 操作认证。
现在常见方法有:
SSH
Personal Access Token
GitHub CLI
Git Credential Manager
2. HTTPS方式
远程地址类似:
https://github.com/user/project.git
如果命令行要求输入 Password:
不是输入GitHub登录密码
而是使用PAT等认证方式
3. SSH方式
远程地址类似:
[email protected]:user/project.git
比较适合:
长期在自己的开发机器上使用
配置完成后:
git pull
git push
都比较方便。
十二、git clone:把远程仓库复制到本地
git clone <repository>
结果大致是:
远程 GitHub 仓库
↓
git clone
↓
本地工作区 + .git仓库
进入:
cd project
即可开始开发。
十三、本地已有项目如何接入Git?
1. 初始化
git init
2. 添加文件
git add .
3. 第一次提交
git commit \
-m "init: initial project"
4. 添加远端
git remote add origin <repository>
查看:
git remote -v
5. 推送
git push -u origin main
具体默认分支可能取决于:
仓库配置
Git版本
项目设置
所以遇到问题先:
git branch
确认本地分支名称。
十四、git status:最应该频繁使用的命令
执行:
git status
可以看到:
当前在哪个分支
哪些文件被修改
哪些文件未跟踪
哪些修改已经暂存
哪些修改还未暂存
初学 Git 时,遇到:
我现在到底什么状态?
第一条命令应该就是:
git status
十五、git diff:提交前看看自己改了什么
1. 工作区与暂存区的差异
git diff
2. 暂存区与最近提交的差异
git diff --cached
因此一个不错的提交习惯是:
修改代码
↓
git diff
↓
git add
↓
git diff --cached
↓
git commit
十六、git log:查看历史
git log
更紧凑:
git log --oneline
例如:
7c19f20 fix: correct calculation
123af92 feat: add progress bar
98cb3ab init: initial project
这里每个提交都有一个:
Commit Hash
用于唯一标识这次提交。
十七、git pull是什么?
git pull
用于:
从远端获取更新
+
整合进当前分支
多人协作时:
先 pull
再开发/提交
是一种常见流程。
但真实项目中还需要进一步理解:
fetch
merge
rebase
之间的关系。
入门阶段先掌握:
clone
status
add
commit
pull
push
log
diff
即可。
十八、.gitignore有什么用?
有些文件不应该提交:
*.o
可执行文件
构建目录
IDE配置
日志文件
临时文件
例如:
*.o
*.out
build/
.vscode/
*.log
之后:
Git默认忽略这些文件
十九、一个C/C++项目的.gitignore示例
# object files
*.o
*.obj
# executables
*.out
*.exe
# build directories
build/
cmake-build-*/
# debug files
*.core
# editor
.vscode/
.idea/
# temporary files
*.swp
*.tmp
二十、为什么Git要有暂存区?
很多人第一次接触 Git 会问:
为什么不能直接:
修改
↓
commit
暂存区的价值在于:
一次工作中可能改了很多东西
但你希望拆成:
Commit 1
修Bug
Commit 2
修改文档
Commit 3
重构函数
于是可以:
选择性 git add
让每个 Commit 更清晰。
甚至:
git add -p
可以按代码块选择:
哪些修改进入下一次提交
二十一、Git分支的基本思想
1. 为什么需要分支?
假设:
main
是稳定版本。
你要开发一个:
progress-bar
新功能。
不希望开发到一半影响主线。
于是:
main
|
o----o
\
o----o feature
2. 创建并切换分支
现代 Git:
git switch -c feature/progress
开发完成后:
git switch main
再根据团队流程:
merge
Pull Request
整合代码。
二十二、从Git进入GDB:版本管理解决不了Bug本身
Git 可以告诉我们:
代码什么时候变坏了
谁改了什么
但要真正回答:
程序为什么得到错误结果?
需要:
Debugger
Linux 下最经典的就是:
GDB
二十三、GDB是什么?
GDB:
GNU Debugger
可以帮助我们:
设置断点
单步执行
查看变量
修改变量
查看栈帧
查看调用链
监视内存或变量变化
二十四、调试之前为什么要加-g?
假设:
gcc main.c -o app
程序当然可以运行。
但是 GDB 很难获得完整的:
源文件
行号
变量名
类型
映射信息。
因此调试版本应该加入:
-g
例如:
gcc main.c \
-g \
-O0 \
-Wall \
-Wextra \
-o app
其中:
-g
↓
生成调试信息
-O0
↓
尽量减少优化造成的调试干扰
也可以根据需求考虑:
-Og
二十五、启动GDB
gdb ./app
进入:
(gdb)
退出:
quit
或者:
Ctrl + D
二十六、list:查看源代码
list
简写:
l
查看:
main函数
list main
查看指定文件:
list main.c:20
二十七、break:设置断点
1. 按行号
break 20
简写:
b 20
2. 按函数
break main
break Sum
3. 文件加行号
break main.c:20
适合:
多文件工程
二十八、查看和管理断点
1. 查看
info breakpoints
常见简写:
i b
2. 删除
删除编号 2:
delete 2
删除全部:
delete
3. 禁用
disable 2
4. 重新启用
enable 2
二十九、run与continue
1. run
run
简写:
r
含义:
从程序开头开始执行
直到:
遇到断点
收到信号
程序结束
2. continue
continue
简写:
c
表示:
从当前停止位置继续运行
直到下一个停止点。
三十、next与step:调试最常用的区别
假设:
int n = Sum(1, 100);
1. next
next
简称:
n
执行:
下一行
但遇到:
函数调用
一般:
不会进入函数内部
2. step
step
简称:
s
遇到:
函数调用
会尝试进入:
函数内部
3. 可以类比IDE
next
≈ Step Over / F10
step
≈ Step Into / F11
三十一、finish:把当前函数执行完
如果已经进入:
Sum()
但不想继续一行一行走:
finish
作用:
运行到当前函数返回
并通常显示返回值。
三十二、until:快速执行到后面
例如:
until 30
可以让程序继续运行到:
指定位置
在调试循环时比较方便。
三十三、print:查看表达式
查看变量:
print result
简称:
p result
也可以计算表达式:
p start + end
甚至:
p array[10]
三十四、display:每次暂停自动显示
display i
之后每次程序停下:
GDB都会自动输出 i
查看 display:
info display
取消:
undisplay 1
三十五、set var:临时修改程序变量
假设程序:
int flag = 0;
导致:
结果错误
调试时可以:
set var flag = 1
再继续运行。
如果结果立刻恢复正常:
说明 flag 很可能就是问题来源
这种方法非常适合:
验证Bug假设
但注意:
修改只发生在当前调试进程
源代码本身并没有被改
三十六、backtrace:查看调用栈
backtrace
简称:
bt
例如:
#0 Divide()
#1 Calculate()
#2 Process()
#3 main()
它能回答:
程序到底是从哪一条调用链走到这里的?
在:
崩溃
段错误
深层函数错误
递归
场景中非常重要。
三十七、info locals:查看当前局部变量
info locals
可以一次看到:
当前栈帧中的局部变量
相比逐个:
p a
p b
p c
更方便。
三十八、watch:变量什么时候被改坏了?
这是一条非常有用的命令。
假设:
result
本来应该保持正确。
但某个地方莫名其妙变了。
你不知道:
是谁改的
可以:
watch result
继续:
continue
当 result 的值发生变化时:
GDB会暂停程序
并显示:
Old value
New value
这属于:
Watchpoint
数据断点
非常适合:
变量被意外修改
内存被错误覆盖
状态莫名变化
的 Bug。GDB 官方文档同样把 watchpoint 定义为在表达式值发生变化时停止程序。
三十九、条件断点
假设:
for (int i = 0;
i < 100000;
++i)
{
Process(i);
}
Bug 只在:
i == 30000
出现。
如果每次都停:
完全不可用
所以可以:
break main.c:20 if i == 30000
程序只有在:
到达20行
并且
i == 30000
时才停止。
GDB 的断点可以附加条件,条件为真时才真正停下,这在循环和高频路径中特别有用。
四十、给已有断点添加条件
假设断点编号:
2
可以:
condition 2 i == 30
于是:
Breakpoint 2
以后只在:
i == 30
时生效。
四十一、GDB最常用命令速查
| 目的 | 命令 |
|---|---|
| 启动 | gdb ./app |
| 查看代码 | list |
| 设置断点 | break |
| 查看断点 | info breakpoints |
| 运行 | run |
| 继续 | continue |
| 单步不进入函数 | next |
| 单步进入函数 | step |
| 执行完当前函数 | finish |
| 查看变量 | print |
| 自动显示变量 | display |
| 修改变量 | set var |
| 查看调用栈 | backtrace |
| 查看局部变量 | info locals |
| 监视变量变化 | watch |
| 删除断点 | delete |
| 退出 | quit |
四十二、一个完整GDB调试案例
1. Bug代码
#include <stdio.h>
int flag = 0;
int Sum(
int start,
int end)
{
int result = 0;
for (int i = start;
i <= end;
++i)
{
result += i;
}
return result * flag;
}
int main(void)
{
int start = 1;
int end = 100;
int ret =
Sum(start, end);
printf(
"%d\n",
ret
);
return 0;
}
我们期待:
5050
实际:
0
2. 编译调试版本
gcc main.c \
-g \
-O0 \
-Wall \
-Wextra \
-o app
3. 启动
gdb ./app
4. main设置断点
b main
r
5. 找到Sum调用位置
l
然后:
s
进入:
Sum
6. 执行循环
可以:
until
或者逐步:
n
7. 查看result
p result
得到:
5050
说明:
求和本身正确
8. 查看flag
p flag
得到:
0
于是我们怀疑:
result * flag
就是问题。
9. 临时修改
set var flag = 1
继续:
n
最终:
5050
此时就基本确认:
Bug来自flag初始值
四十三、GDB调试思路比命令更重要
真正调试时不要:
程序一错
↓
从第一行开始next
↓
一直按几百次
更高效的思路应该是:
观察错误现象
↓
提出假设
↓
确定可疑代码区域
↓
设置断点
↓
观察关键变量
↓
缩小范围
↓
验证假设
GDB 只是:
验证假设的工具
而不是:
自动帮你找到Bug的魔法
四十四、什么是CGDB?
CGDB 可以理解成:
GDB
+
更方便查看源码的终端界面
如果纯 GDB 的:
list
使用起来不够直观,可以尝试:
CGDB
但学习阶段仍建议先掌握原生 GDB 的:
break
run
next
step
print
watch
bt
因为这些调试思想在:
VS
VSCode
CLion
GDB
LLDB
中都是共通的。
四十五、Git和GDB如何形成完整工作流?
可以形成:
拉取代码
↓
git pull
↓
编辑代码
↓
Vim / VSCode
↓
编译
↓
gcc / g++
↓
程序出现Bug
↓
GDB
↓
修复Bug
↓
重新测试
↓
git diff
↓
git add
↓
git commit
↓
git push
这已经是一套最基础的:
真实开发流程
四十六、一套推荐的日常Git工作流程
git status
git pull
# 修改代码
git diff
# 编译和测试
git status
git add .
git diff --cached
git commit \
-m "fix: correct sum calculation"
git push
随着项目复杂度提高,再继续学习:
branch
merge
rebase
stash
tag
reset
revert
cherry-pick
即可。
四十七、Git与GDB最容易混淆的几个问题
1. git add不是提交
git add
↓
暂存
git commit
↓
本地提交
git push
↓
上传远程
2. commit不等于GitHub已经更新
只执行:
git commit
代码仍然主要在:
本地仓库
还需要:
git push
才能同步到远程。
3. GitHub密码不能再直接用于git push
HTTPS:
PAT / GitHub CLI / Credential Manager
或者:
SSH
而不是账户登录密码。
4. 没有-g也能用GDB,但调试体验会很差
没有调试信息时:
源码行号
变量名
函数信息
可能大量缺失。
所以:
-g
是源码级调试的核心选项。
5. next和step区别
next
↓
不进入函数
step
↓
进入函数
这是最需要记住的一组。
6. watch和display不是一回事
display x
↓
每次程序停下时显示x
watch x
↓
x发生变化时让程序停下
四十八、整套Linux开发工具知识图
Linux开发
|
┌───────────────┼───────────────┐
↓ ↓ ↓
编辑 构建 管理
| | |
Vim GCC / G++ Git
|
Makefile
|
↓
运行
|
┌──────┴──────┐
↓ ↓
正常 Bug
|
↓
GDB
|
↓
修复代码
|
↓
Git Commit
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/Face_FeaR/article/details/167082155




