6. 版本控制器Git
不知道你⼯作或学习时,有没有遇到这样的情况:我们在编写各种⽂档时,为了防⽌⽂档丢失,更改
失误,失误后能恢复到原来的版本,不得不复制出⼀个副本,⽐如:
“报告-v1”
“报告-v2”
“报告-v3”
“报告-确定版”
“报告-最终版”
“报告-究极进化版”
...
每个版本有各⾃的内容,但最终会只有⼀份报告需要被我们使⽤ 。
但在此之前的⼯作都需要这些不同版本的报告,于是每次都是复制粘贴副本,产出的⽂件就越来越
多,⽂件多不是问题,问题是:随着版本数量的不断增多,你还记得这些版本各⾃都是修改了什么
吗?
⽂档如此,我们写的项⽬代码,也是存在这个问题的!!
6-1 版本控制器
为了能够更⽅便我们管理这些不同版本的⽂件,便有了版本控制器。所谓的版本控制器,就是能让你
了解到⼀个⽂件的历史,以及它的发展过程的系统。通俗的讲就是⼀个可以记录⼯程的每⼀次改动和
版本迭代的⼀个管理系统,同时也⽅便多⼈协同作业。
⽬前最主流的版本控制器就是 Git 。Git 可以控制电脑上所有格式的⽂件,例如 doc、excel、dwg、
dgn、rvt等等。对于我们开发⼈员来说,Git 最重要的就是可以帮助我们管理软件开发项⽬中的源代码⽂件!
6-2 git 简史
同⽣活中的许多伟⼤事物⼀样,Git 诞⽣于⼀个极富纷争⼤举创新的年代。
Linux 内核开源项⽬有着为数众多的参与者。 绝⼤多数的 Linux 内核维护⼯作都花在了提交补丁和保
存归档的繁琐事务上(1991−2002年间)。 到 2002 年,整个项⽬组开始启⽤⼀个专有的分布式版本
控制系统 BitKeeper 来管理和维护代码。
到了 2005 年,开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束,他们收回了 Linux
内核社区免费使⽤ BitKeeper 的权⼒。 这就迫使 Linux 开源社区(特别是 Linux 的缔造者 Linus
Torvalds)基于使⽤ BitKeeper 时的经验教训,开发出⾃⼰的版本系统。 他们对新的系统制订了若⼲
⽬标:
• 速度
• 简单的设计
• 对⾮线性开发模式的强⼒⽀持(允许成千上万个并⾏开发的分⽀)
• 完全分布式
• 有能⼒⾼效管理类似 Linux 内核⼀样的超⼤规模项⽬(速度和数据量)
⾃诞⽣于 2005 年以来,Git ⽇臻成熟完善,在⾼度易⽤的同时,仍然保留着初期设定的⽬标。 它的速
度⻜快,极其适合管理⼤项⽬,有着令⼈难以置信的⾮线性分⽀管理系统。
6-3 安装 git
yum install git $ git --version git version 1.8.3.16-4 在 Github 创建项⽬
注册账号
这个⽐较简单, 参考着官⽹提⽰即可. 需要进⾏邮箱校验.
创建项⽬
1. 登陆成功后, 进⼊个⼈主⻚, 点击左下⽅的 New repository 按钮新建项⽬![]()
2. 然后跳转到的新⻚⾯中输⼊项⽬名称(注意, 名称不能重复, 系统会⾃动校验. 校验过程可能会花费⼏
秒钟). 校验完毕后, 点击下⽅的 Create repository 按钮确认创建.![]()
3. 在创建好的项⽬⻚⾯中复制项⽬的链接, 以备接下来进⾏下载![]()
下载项⽬到本地
创建好⼀个放置代码的⽬录
git clone [url]这⾥的 url 就是刚刚建⽴好的 项⽬ 的链接.
6-5 三板斧
1. git add
将代码放到刚才下载好的⽬录中
git add [⽂件名] 12. git commit
提交改动到本地
git commit -m "XXX"最后的 "." 表⽰当前⽬录
提交的时候应该注明提交⽇志, 描述改动的详细内容.
3. git push
同步到远端服务器上
git push需要填⼊⽤⼾名密码. 同步成功后, 刷新 Github ⻚⾯就能看到代码改动了.
配置免密码提交
https://blog.csdn.net/camillezj/article/details/55103149
4. 其他
• git log/status/pull
• .ignore
• ⾸次使⽤问题,需要现场看
现在我们更推荐在gitee上面创建自己的代码仓库
在gitee上面创建完仓库以后,我们可以通过指令建立我们的代码仓库
## 一、前期准备
1. 注册并登录 [Gitee](https://link.wtturl.cn/?target=https%3A%2F%2Fgitee.com%2F&scene=im&aid=582478&lang=zh)
2. 新建仓库
- 仓库名称:`linux_git`
- 仓库介绍自定义,**不要勾选初始化 README**(避免后续冲突)
- 仓库创建完成,复制仓库 HTTPS 链接
3. 生成 Gitee 私人令牌(重点!Gitee 不再支持账号密码推送)
右上角头像 → 设置 → 安全设置 → 私人令牌
- 新建令牌,备注随便写,权限勾选:`projects`(仓库读写权限)
- 生成后**立刻复制保存令牌**,页面关闭之后看不到!
## 二、Linux 安装 git
sudo apt update sudo apt install git## 三、配置 Git 全局用户名和邮箱(只需要配置一次)
>这里的用户名邮箱是提交记录显示的,**不一定是 Gitee 账号**
git config --global user.name "" git config --global user.email "你的邮箱" # 查看配置是否生效 git config --global --list四、创建本地代码文件夹,初始化 git 仓库
# 创建并进入工作目录 mkdir lesson1 cd lesson1 # 初始化本地Git仓库(生成隐藏文件夹 .git) git init五、关联远程 Gitee 仓库(只执行一次)
把链接替换成你自己仓库的 HTTPS 地址
git remote add origin https://gitee.com/xxx/linux_git.git # 查看远程关联是否成功 git remote -v六、编写代码文件(示例 test.c)
#include <stdio.h> int main() { printf("Hello Git & Gitee!\n"); return 0; }七、Git 三步提交(本地操作)
# 1. 添加文件到暂存区 git add test.c # 添加当前目录全部文件写法:git add . # 2. 提交到本地仓库,-m 后面写提交备注 git commit -m "第一次提交:上传test.c测试文件"八、推送到远程 Gitee 仓库
# 第一次推送需要 -u 建立本地分支和远程分支绑定关系 git push -u origin masterUsername for 'https://gitee.com':九、后续修改代码后的推送流程(日常使用)
git add . git commit -m "备注:修改了xxx功能" git push十、可选:配置凭证缓存(不用每次都输账号令牌)
# 缓存凭证1小时 git config --global credential.helper cache## 十一、常见报错 & 坑点总结
1. Password 填登录密码报错:Gitee 现在禁用密码推送,**必须使用私人令牌**
2. 提示 fatal: remote origin already exists:之前绑定过远程,执行 `git remote remove origin`
3. 仓库初始化时勾选了 README,第一次 push 会报分支冲突,解决方案:
git pull origin master --allow-unrelated-histories十二、拓展常用命令
git log # 查看所有提交记录 git status # 查看当前文件状态(红色未暂存、绿色已暂存) git pull # 拉取远程仓库最新代码到本地一、第一次上传(**全新仓库,本地还没有 git 仓库**)
本地文件夹第一次关联远程 Gitee 仓库,需要:初始化 + 关联远程 + add + commit + push
# 1. 初始化本地git仓库(只执行一次!) git init # 2. 关联Gitee远程仓库地址(只执行一次!) git remote add origin https://gitee.com/xue-zhongkun/linux_git.git # 3. 添加文件到暂存区 git add test.c # 4. 本地提交,写备注 git commit -m "第一次提交:上传test.c源码" # 5. 推送到远程Gitee git push -u origin master1. `git init`、`git remote add` **只需要第一次做**,后面不用重复敲
2. `-u`(--set-upstream)作用:绑定本地 master 和远程 master 分支,**第一次 push 带 - u**
3. 第一次推送时,需要输入 Gitee 账号 + 私人令牌作为密码
后续上传(仓库已经建好、关联好了,改代码 / 新增文件)
# 1. 添加新增/修改的源码 git add xue.c # 2. 本地提交备注 git commit -m "main的测试,新增xue.c" # 3. 推送远程(不用加 -u origin master!直接git push) git push直接在当前目录下进行新建是没法上传的要加入到git的缓存区
commit以后才是提交到了仓库
-g选项以后才能进入debug模式
7. 调试器 - gdb/cgdb使⽤
7-1 样例代码
// mycmd.c #include <stdio.h> int Sum(int s, int e) { int result = 0; for(int i = s; i <= e; i++) { result += i; } return result; } int main() { int start = 1; int end = 100; printf("I will begin\n"); int n = Sum(start, end); printf("running done, result is: [%d-%d]=%d\n", start, end, n); return 0; }7-2 预备
程序的发布⽅式有两种, debug 模式和 release 模式, Linux gcc/g++ 出来的⼆进制程
序,默认是 release 模式。
• 要使⽤gdb调试,必须在源代码⽣成⼆进制程序的时候, 加上 -g 选项,如果没有添加,程序⽆法被
编译
$ gcc mycmd.c -o mycmd # 默认模式,不⽀持调试 $ file mycmd mycmd: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=82f5cbaada10a9987d9f325384861a88d278b160, for GNU/Linux 3.2.0, not stripped $ gcc mycmd.c -o mycmd -g # debug模式 $ file mycmd mycmd: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=3d5a2317809ef86c7827e9199cfefa622e3c187f, for GNU/Linux 3.2.0, with debug_info, not stripped7-3 常⻅使⽤
• 开始: gdb binFile
• 退出: ctrl + d 或 quit 调试命令
7-4 常⻅技巧 - 加餐
📌 安装cgdb:
• 上⾯的基本调试还是⿇烦,虽然是⿊屏,但是还是想看到代码调试
• 推荐安装cgdb:
• Ubuntu: sudo apt-get install -y cgdb
• Centos: sudo yum install -y cgdb
7-4-1 watch
执⾏时监视⼀个表达式(如变量)的值。如果监视的表达式在程序运⾏期间的值发⽣变化,GDB 会暂
停程序的执⾏,并通知使⽤者
(gdb) l main 11 12 return result; 13 } 14 15 int main() 16 { 17 int start = 1; 18 int end = 100; 19 printf("I will begin\n"); 20 int n = Sum(start, end); (gdb) b 20 Breakpoint 1 at 0x11c3: file mycmd.c, line 20. (gdb) info b Num Type Disp Enb Address What 1 breakpoint keep y 0x00000000000011c3 in main at mycmd.c:20 (gdb) r Starting program: /home/whb/test/test/mycmd I will begin Breakpoint 1, main () at mycmd.c:20 20 int n = Sum(start, end); (gdb) s Sum (s=32767, e=-7136) at mycmd.c:5 5 { (gdb) n 6 int result = 0; (gdb) watch result Hardware watchpoint 2: result (gdb) c Hardware watchpoint 2: result Old value = -6896 New value = 0 Sum (s=1, e=100) at mycmd.c:7 7 for(int i = s; i <= e; i++) (gdb) c Continuing. Hardware watchpoint 2: result Old value = 0 New value = 1 Sum (s=1, e=100) at mycmd.c:7 7 for(int i = s; i <= e; i++) (gdb) c Continuing. Hardware watchpoint 2: result Old value = 1 New value = 3 Sum (s=1, e=100) at mycmd.c:7 7 for(int i = s; i <= e; i++) (gdb) c Continuing. Hardware watchpoint 2: result Old value = 3 New value = 6 Sum (s=1, e=100) at mycmd.c:7 7 for(int i = s; i <= e; i++) (gdb) info b Num Type Disp Enb Address What 1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c:20 breakpoint already hit 1 time 2 hw watchpoint keep y result breakpoint already hit 4 times (gdb) d 2 (gdb) info b Num Type Disp Enb Address What 1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c:20 breakpoint already hit 1 time (gdb) finish Run till exit from #0 Sum (s=1, e=100) at mycmd.c:7 0x00005555555551d2 in main () at mycmd.c:20 20 int n = Sum(start, end); Value returned is $1 = 5050(gdb) l main # l = list,查看main附近源代码 11 12 return result; 13 } 14 15 int main() 16 { 17 int start = 1; 18 int end = 100; 19 printf("I will begin\n"); 20 int n = Sum(start, end); (gdb) b 20 # b = breakpoint,在第20行打断点(调用Sum这一行) Breakpoint 1 at 0x11c3: file mycmd.c, line 20. (gdb) info b # info breakpoint:查看所有断点信息 Num Type Disp Enb Address What 1 breakpoint keep y 0x00000000000011c3 in main at mycmd.c:20 (gdb) r # r = run,运行程序,直到碰到断点 Starting program: /home/whb/test/test/mycmd I will begin Breakpoint 1, main () at mycmd.c:20 20 int n = Sum(start, end); # 程序停在断点,准备进入Sum函数 (gdb) s # s = step,单步进入函数(进入Sum内部) Sum (s=32767, e=-7136) at mycmd.c:5 5 { (gdb) n # n = next,单步执行,不进入子函数 6 int result = 0; (gdb) watch result # watch:设置硬件观察点,变量result一旦发生修改就停下 Hardware watchpoint 2: result (gdb) c # c = continue,继续运行程序,直到下一个断点/观察点触发 Continuing. Hardware watchpoint 2: result Old value = -6896 New value = 0 # result被初始化为0,触发观察点 Sum (s=1, e=100) at mycmd.c:7 7 for(int i = s; i <= e; i++) (gdb) c # 继续运行,等待result再次变化 Continuing. Hardware watchpoint 2: result Old value = 0 New value = 1 # result = 0+1 Sum (s=1, e=100) at mycmd.c:7 (gdb) c Continuing. Hardware watchpoint 2: result Old value = 1 New value = 3 # result = 1+2 Sum (s=1, e=100) at mycmd.c:7 (gdb) c Continuing. Hardware watchpoint 2: result Old value = 3 New value = 6 # result = 3+3 Sum (s=1, e=100) at mycmd.c:7 (gdb) info b # 再次查看断点/观察点,看到观察点已经命中4次 Num Type Disp Enb Address What 1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c:20 breakpoint already hit 1 time 2 hw watchpoint keep y result breakpoint already hit 4 times (gdb) d 2 # d = delete,删除编号为2的观察点 (gdb) info b # 查看断点,观察点2已经删掉,只剩断点1 Num Type Disp Enb Address What 1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c:20 breakpoint already hit 1 time (gdb) finish # finish:直接运行到当前Sum函数执行结束、return退出 Run till exit from #0 Sum (s=1, e=100) at mycmd.c:7 0x00005555555551d2 in main () at mycmd.c:20 20 int n = Sum(start, end); Value returned is $1 = 5050 # Sum返回结果:1~100累加和 5050这个是在上面出现的调试信息的功能
• 如果你有⼀些变量不应该修改,但是你怀疑它修改导致了问题,你可以watch它,如
果变化了,就会通知你.
• 监视变量的变化
7-4-2 set var确定问题原因
更改⼀下标志位,假设我们想得到 +-result
// mycmd.c #include <stdio.h> int flag = 0; // 故意错误 //int flag = -1; //int flag = 1; int Sum(int s, int e) { int result = 0; for(int i = s; i <= e; i++) { result += i; } return result*flag; } int main() { int start = 1; int end = 100; printf("I will begin\n"); int n = Sum(start, end); printf("running done, result is: [%d-%d]=%d\n", start, end, n); return 0; } ) l main 15 16 return result*flag; 17 } 18 19 int main() 20 { 21 int start = 1; 22 int end = 100; 23 printf("I will begin\n"); 24 int n = Sum(start, end); (gdb) b 24 Breakpoint 1 at 0x11ca: file mycmd.c, line 24. (gdb) r Starting program: /home/whb/test/test/mycmd I will begin Breakpoint 1, main () at mycmd.c:24 24 int n = Sum(start, end); (gdb) n 25 printf("running done, result is: [%d-%d]=%d\n", start, end, n); (gdb) n running done, result is: [1-100]=0 # 这⾥结果为什么是0? 26 return 0; (gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/whb/test/test/mycmd I will begin Breakpoint 1, main () at mycmd.c:24 24 int n = Sum(start, end); (gdb) s Sum (s=32767, e=-7136) at mycmd.c:9 9 { (gdb) n 10 int result = 0; (gdb) n 11 for(int i = s; i <= e; i++) (gdb) 13 result += i; (gdb) 11 for(int i = s; i <= e; i++) (gdb) 13 result += i; (gdb) until 14 Sum (s=1, e=100) at mycmd.c:16 16 return result*flag; (gdb) p result $1 = 5050 (gdb) p flag $2 = 0 (gdb) set var flag=1 # 更改flag的值,确认是否是它的原因 (gdb) p flag $3 = 1 (gdb) n 17 } (gdb) n main () at mycmd.c:25 25 printf("running done, result is: [%d-%d]=%d\n", start, end, n); (gdb) n running done, result is: [1-100]=5050 # 是它的原因 26 return 0;条件断点添加常⻅两种⽅式:1. 新增 2. 给已有断点追加
• 注意两者的语法有区别,不要写错了。
• 新增: b ⾏号/⽂件名:⾏号/函数名 if i == 30 (条件)
• 给已有断点追加: condition 2 i==30 , 其中 2 是已有断点编号,没有if
• Cgbd 分屏操作 ESC 进⼊代码屏, i 回到 gdb 屏
在平时调试的时候建议打开cgdb因为这样可以最直观的看到断点和代码运行的顺序
加上断点的时候后面的数字是行号,去掉的时候是断点的序号
p result # 打印result的值 set var result=10 # 在gdb里直接修改变量的值 bt # backtrace 查看函数调用栈(看一层层是谁调用的)Centos7更新yum源
在CentOS 7中更新Yum源可以通过以下步骤完成:
1. 备份现有Yum源
在更新之前,建议备份现有的Yum源配置⽂件:
sudo mkdir /etc/yum.repos.d/backup sudo mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/backup/2. 下载新的Yum源配置⽂件
CentOS 7默认使⽤CentOS官⽅源,如果需要使⽤其他源(如阿⾥云、清华源等),可以⼿动下载对应
的配置⽂件。
使⽤阿⾥云源
sudo curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo使⽤清华源
sudo curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.tuna.tsinghua.edu.cn/repo/Centos-7.repo3. 清理并⽣成缓存
更新Yum源后,清理旧的缓存并⽣成新的缓存:
sudo yum clean all sudo yum makecache4. 更新系统
更新Yum源后,可以更新系统以确保所有软件包都是最新的:
sudo yum update 15. 恢复原有Yum源(可选)
如果需要恢复原有的Yum源,可以将备份的配置⽂件移回原⽬录:
udo mv /etc/yum.repos.d/backup/*.repo /etc/yum.repos.d/ 16. 验证Yum源
可以通过以下命令验证Yum源是否更新成功:
sudo yum repolistubuntu的更新逻辑类似,如果必要后续会出教程。在下一章我们将进行进程的基本概念讲解,我打算分多节内容彻底给大家讲解完进程的概念和实践应用