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

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