Skip to content

L6 Git 协作

本课目标

建立“检查状态 -> 暂存 -> 提交 -> 推送”的日常循环,能在独立分支完成修改并发起 Pull Request,同时理解同步和冲突处理的边界。

为什么我们需要 Git?

在正式敲命令之前,我想先问大家两个非常现实的问题。

痛点一:毕业论文式的文档命名 大家在写实验报告或课程设计文档的时候,是不是经常经历这样的心路历程:

  1. 设计方案_v1.docx (初稿完成)

  2. 设计方案_v2_修改.docx (加了文献,改了格式)

  3. 设计方案_final.docx (一定是最终版)

  4. 设计方案_final_打死不改.docx (真的最终版)

  5. 设计方案_final_打死不改_老师又让改.docx (导师反馈,再改一遍)

最绝望的是什么?当你改得乱七八糟,甚至删掉了某个重要章节,Ctrl+Z 的撤销历史也已经没了,你突然想找回上周五那个“文献还没加但逻辑清楚”的版本,却翻出来 5 个 final 都不知道哪个是想要的。

痛点二:U盘传代码的恐惧

打比赛是团队合作。学长在调底盘的底盘解算,你在写云台的视觉通信。你们俩要把代码合并到一辆车上测试。 “喂,学长,我把你昨天的代码用微信发给你,你把我的 main.c 复制进去,千万别覆盖了你改的第 150 行啊!” 结果一跑,车炸了。谁也不知道到底是谁改错了哪行代码。

结论:你需要 Git。 Git 提供可追溯的版本历史,也能让多人在独立分支上协作并审查合并。


终端常用命令

我们要学 Git,就不可避免地要和黑框框(终端 Terminal)打交道。忘掉鼠标双击,让我们像程序员一样和计算机对话。 大家打开电脑上的终端:

image.png

  • pwd(Print Working Directory):显示当前目录。

  • ls(List):列出当前目录中的文件。

  • cd <文件夹名>(Change Directory):切换目录,例如 cd Desktopcd .. 返回上一级。

  • mkdir <文件夹名>(Make Directory):创建目录。

互动小练习: 用终端在桌面文件夹里新建一个叫 RoboMaster_Git_Lab 的文件夹,并走进去。


前置任务:自报家门 (git config)

在第一次使用 Git 之前,你必须完成极其重要的一步:告诉 Git 你是谁。 为什么?因为以后出了 Bug,Git 的历史记录里要清清楚楚地写明这行炸车的代码是谁写的(俗称“精准追溯”)。

在终端敲入这两行(替换成你自己的英文拼音和邮箱):

bash
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"

这一步在你的电脑上只需要做一次,终身有效。

⚠️ 重要提醒 上面的两行命令千万不要直接全选粘贴! 请一行一行地来:第一步,敲写(或粘贴)第一行:注意把 你的名字拼音 改成自己的英文名字拼音(比如 Zhang San 或 zhangsan),保留引号,然后按回车。第二步,敲写(或粘贴)第二行:把 你的邮箱@example.com 改成自己的真实邮箱(比如 zhangsan@qq.com),然后按回车。


核心循环:雷达、取景框与快门

现在,我们站在了刚才新建的 RoboMaster_Git_Lab 文件夹里。这里目前只是个普通的文件夹,还没有任何版本控制。

第一步:初始化仓库 git init

bash
git init

敲下回车,你会看到提示 Initialized empty Git repository。 此时,Git 已经在你文件夹里建了一个隐藏的 .git 文件夹。从现在起,这个文件夹里发生的任何改动,都逃不过 Git 的眼睛。

第二步:Git 界的雷达 git status (极其重要!)

只要你感到迷茫,不知道刚才敲的命令生效没,直接敲:

bash
git status

现在我们新建一个 main.c 文件随便写两行字。再敲 git status,你会看到红色的字写着 Untracked files: main.c 红字的意思是:Git 看到这里有个新文件,但还没把它纳入版本控制。

不是什么都要提交

.gitignore 中加入 cmake-build-debug/、构建输出和本地 IDE 配置。它们可以重新生成,提交后反而容易制造无关差异。已经被 Git 跟踪的文件仅写入 .gitignore 不会自动取消跟踪。

第三步:放入取景框 git add

准备好你要存档的文件后,我们要把它们放进“准备提交的取景框”(暂存区)。

bash
git add main.c   # 只加这一个文件
git add .        # (最常用)把当前目录下所有红字文件全部加入暂存区

再次敲 git status,你会发现原本的红字变成了绿字。绿字代表:这些文件已经准备好,可以提交了!

第四步:按下快门 git commit

bash
git commit -m "完成底盘PID基础框架"

-m 后面跟着的,是这次提交的“留言”。千万不要写“111”或者“改代码”这种无意义的描述! 一定要写清楚你做了什么改动。 按下回车,一个完整的版本快照就永久地保存在了本地仓库里。即使你明天把代码删得一干二净,也能瞬间找回来。


时光机:后悔药怎么吃?

刚刚我们提交了快照,那怎么看我们提交过的历史呢?

查看历史:git log

bash
git log

你会看到一串长长的、像乱码一样的黄字(比如 commit a1b2c3d4...),这就是这次提交的唯一标识符 (Commit Hash),下面还写着谁在什么时间,提交了什么留言。

时光倒流:git reset / git restore

如果你刚刚把代码写乱了,还没提交:

这会丢弃未提交修改

先运行 git statusgit diff,确认这些改动确实不再需要。无法从 Git 恢复从未提交过的内容。

bash
# 把工作区所有未保存的改动全部撤销,恢复到上一次提交后的状态
git restore .

如果更惨,你已经连续提交了 3 次垃圾代码,想回到昨天那个 a1b2c3d4 的版本:

这会同时移动分支并覆盖工作区

先确认目标提交,必要时先创建临时分支或提交当前工作。不要在不理解影响时执行 --hard

bash
# 警告:这是一剂猛药,会将代码完全回退到那个时刻
git reset --hard a1b2c3d4

平行宇宙:分支 (Branch) 的艺术

Git 最强大的功能,叫做分支 (Branch)

想象一下 RoboMaster 战车代码:main(或旧仓库中的 master)分支代表随时可以集成测试的稳定版本。开发尚未验证的新功能时,应先创建独立分支,避免阻塞队友的底盘和云台测试。

怎么办?拉出一个平行分支!

bash
# 创建并切换到一个叫 "feature/aim-bot" 的新分支
git checkout -b feature/aim-bot

分支上的提交不会直接改变主分支。功能经过构建、测试和审查后,再通过合并(Merge)进入 main

尽早开分支,主干保平安。哪怕只是改一个按键映射,也请养成开新分支的习惯!


图形界面集成:CLion 中的 Git 操作

好,听完上面那些命令行的介绍,现在告诉大家一个好消息: 在实际开发中,你几乎不需要在终端里敲这些命令!

现代 IDE 早已把 Git 深度集成到了图形界面中。刚才我们学的底层逻辑,在 CLion 里都变成了直观的按钮:

  1. git add + git commit:打开 CLion 的 Commit 窗口,勾选本次相关文件、检查差异、填写提交信息并提交。

    image.png

  2. git log:在 CLion 的 Git 工具窗口查看提交时间线、分支和文件差异。

    image.png

  3. git switch -c(分支管理):点击当前分支名并选择 New Branch,或在终端执行 git switch -c feature/name

image.png

为什么有了图形界面还要教命令? 因为只有理解了工作区、暂存区、本地仓库这些概念,你在使用图形界面时遇到问题才不会手足无措。图形界面是命令的快捷方式,而原理才是内功。

远程仓库:为什么需要云端?

上半场我们学会了在自己的电脑上保存版本和创建分支。但问题来了:如果你的电脑突然坏了怎么办?如果你要和另外三个队友一起写同一辆车的代码怎么办?

我们需要一个 “云端的中央仓库”。常见代码托管平台包括 GitHubGitee(码云);它们负责保存 Git 仓库,并提供代码审查、议题和自动化工作流等协作功能。


配置身份验证:告别重复输入密码

在我们把代码推送到云端之前,需要先解决身份验证的问题。 为了防止别人篡改你的代码,代码托管平台在你每次上传时都会要求验证身份。如果每次都要输入账号密码,会非常麻烦。

为了让 Git 记住我们的凭证,请执行以下命令:

bash
# Windows 版 Git 通常已安装并启用 Git Credential Manager
git config --global credential.helper manager

不要把账号密码或访问令牌直接写进仓库、脚本或 credential.helper store 的明文文件中。首次推送时按系统提示登录即可。

注册并登录Gitee账号

  1. 访问gitee官网:https://gitee.com/,点击右上角注册,填写相关信息

    image.png

  2. 酌情进行初始化配置,可以全部点击右下角按钮跳过

    image.png

  3. 进入工作台,点击右上角的绑定邮箱(“添加绑定”这四个字)

    image.png

  4. 点击新增,输入密码验证,输入邮箱

    image.png

    image.png

  5. 在邮箱中查询已经收到的验证邮件,点击确认绑定

image.png

image.png

创建一个云端仓库

  1. 点击右上角的加号->新建仓库

    image.png

  2. 输入仓库相关信息,点击创建

    image.png

  3. 创建完成,进入空仓库页面

image.png

将本地仓库推送到云端 (git push)

在本地新建一个叫 hello-world 的 C++ 项目。这个项目会检测项目文件夹下的“greetings”文件夹里的txt文件,并将其内容输出出来。

现在我们完成了第一次提交。我们把它推送到我在 Gitee 上刚刚建好的空仓库里。

仓库链接:https://gitee.com/dlmu-cone/hello-world

按照gitee的指示,在命令行里执行了下面的操作:

bash
git remote add origin https://gitee.com/dlmu-cone/hello-world.git
git push -u origin "master"

现在,大家刷新一下上面的 Gitee 仓库链接,是不是看到我刚才写的 main.c 已经出现在网页上了? 这就是 Push 的作用:把本地的提交历史,完整地复制到云端。


互动挑战:在开源世界留下你的名字

现在,我要邀请在座的所有人,参与到这个 HelloWorld 项目的开发中来。你们的任务是:在greetings文件夹里加入一个自己的txt文件,并把它合并到我的主分支里。

但这引出了一个权限问题:你们不是这个仓库的拥有者,Gitee 不会允许你们直接把代码 Push 到我的仓库。那开源社区成千上万的程序员是怎么给 Linux 这样的项目贡献代码的?

答案就是:Fork + PR 工作流

Step 1: Fork (复刻)

请大家打开我的 Gitee 仓库页面,点击右上角的 “Fork” 按钮。

image.png

出现下面的窗口,选中个人,然后点击确认

image.png

Fork 的意思是“复刻”。点击之后,Gitee 会在服务器上把我的整个仓库复制一份,放到你自己的账号下。 现在,你拥有了一个完全属于你的云端仓库,你可以对它进行任意修改!

Step 2: Clone (克隆到本地)

现在,打开你自己的 Gitee 页面,找到你刚刚 Fork 下来的 HelloWorld 仓库。点击“克隆/下载”按钮,复制 HTTPS 链接

image.png

image.png

回到 CLion,点击顶部的 Git -> 克隆(或者在欢迎界面点击 克隆项目),粘贴这个链接。

image.png

image.png

image.png

注意,这里的链接一定是Fork的,你自己用户下的仓库链接!

克隆完成后,会提示打开新克隆好的工程。

image.png

或者,你也可以直接在命令行里输入:

bash
# 对应的底层命令:把云端仓库下载到本地
git clone https://gitee.com/你的账号/hello-world.git

(如果提示输入账号密码,先输入你在 Gitee 的手机号并回车,然后输入密码并回车即可,Git 会帮你记住!)

Step 3: 创建自己的开发分支 (Branch)

好习惯从现在养成:不要在 master 上直接修改。 在 CLion 左上角点击 master,选择 新建分支,起名叫 feature/你的拼音(比如 feature/zhangsan)。

image.png

image.png

点击创建,即可在Git选项卡里看到你创建的分支

image.png

Step 4: 提交修改 (Commit)

右键greetings目录,点击新建文件;

image.png

写入自己想要的名称,按下回车

image.png

在文件里输入你的问候或者任意一句话!

image.png

点击 CLion 左侧的蓝色勾 (Commit),填写提交信息:“Add Zhang San greeting”,点击 提交!

image.png

Step 5: 推送到自己的云端仓库 (git push)

点击 CLion 左上角的分支,选中当前我们所在的feature/zhangsan分支,点击推送:

image.pngimage.png

第一次推送的时候,CLion会提示我们输入用户名和密码。我们点击选中下面的使用凭证帮助程序,点击登录,然后在接下来出现的窗口中输入Gitee手机号和密码,点击Continue

image.png

你会发现,代码被推到了你自己的 Gitee 仓库的 feature/zhangsan 分支上。

image.png

image.png

Step 6: 发起 Pull Request (PR)

怎么把你的代码提交给我?这就是开源界著名的 PR (拉取请求)。 回到你自己的 Gitee 仓库网页,点击上方的Pull Request。原分支选择我们创建的新分支,目标分支选择原仓库的master分支。我们可以填写PR描述,然后点击左下角的创建 Pull Request

image.png

:“你好,我在我的分支上完成了一个修改,请你审核一下,并把它拉取 (Pull) 合并到你的主分支中吧!”

仓库负责人在收到这个PR之后,就可以开始决定是否要通过并合并这个PR。

image.png


同步更新:拉取队友的最新代码 (git pull)

大家刚才提交的代码已经被我合并到了主仓库中。 但是大家看看自己电脑上的 CLion 代码,是不是还只有你自己的名字?因为你的本地仓库还没有更新。

首先我们要更新一下云端的Fork仓库。在你自己的Fork仓库中点击拉取:

image.png

接下来,我们需要把云端最新的 master 分支拉取下来。 在 CLion 里,点击分支中的master,点击更新:

image.png

更新完成后再点击签出:

image.png

或者使用命令:

bash
git pull origin master

瞬间,所有队友刚才写的 printf 就出现在了你的电脑上!这就是团队协作的体验。


解决冲突:当两个人改了同一行代码

但是,实际协作中难免会遇到一种情况:代码冲突 (Conflict)

假设,学长在 main.c 的第 20 行写了 speed = 100; 并推送到了云端。 而你,在自己电脑的 main.c 第 20 行写了 speed = 200;。 当你执行 git pull 准备合并学长的代码时,Git 会提示冲突: CONFLICT (content): Merge conflict in main.c

Git 说:“同一行代码有两个不同的修改,我不知道该保留哪个,需要你来决定。”

如果是在命令行里解决冲突,你可能会看到满屏的 <<<<<<< HEAD 和 ======= 标记。 但是,CLion 提供了可视化的冲突解决工具!

当冲突发生时,CLion 会弹出 Conflicts 窗口。点击 Merge (合并): 你会看到屏幕分成三块:

  • 左边 (Your version): 你自己写的 speed = 200;

  • 右边 (Server version): 学长写的 speed = 100;

  • 中间 (Result): 最终合成的代码(冲突行目前是空的)

怎么解决? 点击中间面板两侧的箭头:点左边的 >> 接受你的修改,点右边的 << 接受学长的修改。如果你想同时保留两行,可以手动编辑中间框。 当中间框里没有红色冲突标记时,点击右下角的 Apply (应用)。 冲突就解决了!Git 会自动生成一个合并提交。

最近更新