Git 是什么?
不要把 Git 当作一个枯燥的"版本控制工具",请把它想象成:
时光机:你可以随时回到代码的过去(昨天、上个月、甚至一年前的状态)。
平行宇宙(分支):你可以同时开发"新功能"和"修复Bug",互不干扰,最后合并。
后悔药:删错文件了?改乱代码了?一个命令就能还原。
一、Git 核心概念
在学用法前,先吃透 Git 的核心设计,能避免后续操作踩坑:
概念 通俗解释 工作区 你电脑上实际写代码的文件夹(能看到、能修改的文件都在这里) 暂存区(索引) 临时存放待提交的修改,是 "工作区" 和 "本地仓库" 的中间层(通过 git add填充)本地仓库 本地的版本数据库,保存所有 commit记录(版本快照),.git文件夹就是仓库远程仓库 云端的仓库(如 GitHub/GitLab/Gitee),用于多人协作同步代码 commit 版本快照,是 Git 最小的可追溯单元(包含修改内容、作者、时间、备注) 分支(branch) 独立的开发线,比如 main(主分支)、dev(开发分支)、feature(功能分支)HEAD 指针,指向当前所在的分支 / 版本(可以理解为 "你现在的位置") 哈希值(SHA-1) 每个 commit 唯一的 ID(40 位字符,通常用前 7 位即可)
修改(Modified)和新建(New)的文件都需要 git add。
区别在于: "未追踪"是 Git 根本不认识 的文件;"未暂存"是 Git 认识但觉得你还没改好的文件。
二、Git 基础操作
初始化 / 克隆仓库
初始化本地仓库(从零创建项目):
bash# 进入项目文件夹 cd your-project-folder # 初始化 Git 仓库 git init(此时文件夹下会多出一个隐藏的 .git 文件夹,千万别删)
克隆远程仓库(复制已有项目):
bash# 克隆 HTTPS 地址(无需配置密钥) git clone https://github.com/xxx/xxx.git # 克隆 SSH 地址(需配置密钥,更安全) git clone git@github.com:xxx/xxx.git第一次安装后的自报家门
Git 需要知道是谁在提交代码,否则以后出了 Bug 找不到背锅侠。
bash# 全局配置(设置一次即可) git config --global user.name "你的名字" git config --global user.email "你的邮箱" # 查看配置 git config --list
基础提交流程(工作区 → 暂存区 → 本地仓库)
这是你每天要敲几十遍的命令组合。
1. 查看状态 (最重要!)
不知道现在发生了什么?随时敲这个命令。
git status
红色文件:被修改了,还在工作区。
绿色文件:已经 add 了,在暂存区,等待提交。
2. 添加到暂存区 (add)
bashgit add . # 把当前目录下所有修改放入暂存区(最常用) git add main.cpp # 只把 main.cpp 放入暂存区 git add -u # 只添加已跟踪文件的修改(不包含新增文件)3. 提交存档 (commit)
这会生成一个版本快照。必须写备注!
git commit -m "修复了坦克血条不显示的Bug"4. 查看历史 (log)
git log # 查看详细历史(作者、时间、哈希值) git log --oneline # 简洁模式(只看版本号和备注)5. 关联远程仓库 (remote)
bash# 关联远程仓库(首次推送时需要) git remote add origin 远程仓库地址 # origin 是远程仓库的默认别名 # 查看已关联的远程仓库 git remote -v
分支管理 (Branching) ------ 进阶核心
分支是 Git 的杀手锏。想象一下,你正在开发"坦克开火"功能,突然老板让你修一个"菜单打不开"的 Bug。
你不需要回退代码,只需要切一个分支。
1. 分支操作
bash# 查看所有分支(* 表示当前分支) git branch # 本地分支 git branch -r # 远程分支 git branch -a # 所有分支(本地+远程) # 创建分支 git branch dev # 创建 dev 分支(仅创建,不切换) git checkout -b dev # 创建并切换到 dev 分支(常用) git switch -c dev # Git 2.23+ 新增,等价于 checkout -b # 切换分支 git checkout main # 切换到 main 分支 git switch main # Git 2.23+ 新增,更直观 # 拉取远程新分支到本地 git checkout -b dev origin/dev # 跟踪远程 dev 分支
2. 合并分支 (Merge)
假设你在 feature-fire 分支上开发完了,要把它合并回 main 主分支。
先切回主分支:
git checkout main执行合并:
bash# 合并 dev 到当前分支 git merge dev删除分支:
bashgit branch -d dev # 删除本地 dev 分支(已合并的分支) git branch -D dev # 强制删除本地 dev 分支(未合并也能删) git push origin --delete dev # 删除远程 dev 分支
3. 解决冲突 (Conflict)
这是新手最怕的环节。
当两个人(或者两个分支)同时修改了同一个文件的同一行代码时,Git 无法决定保留谁的,就会报错 CONFLICT。
解决步骤:
打开报错的文件。
你会看到类似这样的符号:
<<<<<<< HEAD Health = 100; // 你的代码 ======= Health = 200; // 别人的代码 >>>>>>> feature-fire人工决定:删掉 <<<, ===, >>> 这些符号,保留你想要的代码(比如保留 100,或者改成 150)。
保存文件。
再次执行 git add . 和 git commit。
远程协作 (Remote)
1. 关联远程仓库
如果你是本地 git init 的,需要告诉 Git 远程地址在哪里。
git remote add origin https://github.com/你的账号/你的仓库.git # origin 是给远程地址起的别名,行业惯例2. 推送 (Push)
把本地的存档上传。
git push -u origin main # -u 的意思是把本地 main 和远程 main 绑定,下次直接 git push 就行3. 拉取 (Pull)
上班第一件事,先把同事写的代码拉下来。
git pull
撤销操作
-
工作区 = 红色 (git status 显示的颜色) = 还没 add。
-
暂存区 = 绿色 (git status 显示的颜色) = add 了,但还没 commit。
撤销工作区的修改
(我刚写的代码写烂了,还没add, 想回到之前的状态):
bashgit checkout -- filename # 恢复文件到最近一次 commit 状态 # 或者新版命令 git restore filename撤销暂存区的修改
(我手滑 add 了,想拿出来):
git reset HEAD filename # 或者新版命令 git restore --staged filename版本回退
(彻底回退到上一个版本,慎用):
bashgit reset --hard HEAD^ # 回退到上一个版本 # 本地回滚(保留修改) git reset --soft 哈希值 # 回滚 commit,保留工作区/暂存区 # 本地回滚(清空修改,谨慎) git reset --hard 哈希值 # 彻底回到指定版本,删除所有未提交修改 # 远程回滚(安全方式,推荐) git revert 哈希值 # 新增反向 commit,抵消指定版本的修改 git push origin main
临时保存手头工作
(我要去修Bug,但现在代码还没写完,不想 commit):
bash# 暂存当前工作区修改 git stash save "暂存登录功能的未完成修改" git stash #系统自动贴个默认标签(通常是你上一次 commit 的名字) # 查看所有暂存记录 git stash list # 恢复最近一次暂存(保留暂存记录) git stash apply stash@{0} # stash@{0} 是第一条暂存记录,可省略 # 恢复并删除暂存记录(常用) git stash pop # 删除指定暂存记录 git stash drop stash@{0} # 删除所有暂存记录 git stash clear
标签管理(标记重要版本,如发布版本)
bash# 创建标签(打标签,比如 v1.0 发布版本) git tag v1.0 # 轻量标签(仅标记) git tag -a v1.0 -m "v1.0 正式发布" # 附注标签(包含备注、作者,推荐) # 查看所有标签 git tag # 推送标签到远程 git push origin v1.0 # 推送单个标签 git push origin --tags # 推送所有标签 # 删除标签 git tag -d v1.0 # 删除本地标签 git push origin --delete v1.0 # 删除远程标签
三、Git 核心特性
1. 分布式版本控制(核心优势)
- 本地完整保留所有版本记录,无需联网即可提交、查看历史、切换分支;
- 即使远程仓库宕机,本地仓库仍能正常工作,恢复后同步即可;
- 多人协作时,可先本地提交,再统一推送到远程,减少冲突概率。
2. 快照式存储(而非增量存储)
- SVN 等工具是 "增量存储"(只记录每次修改的差异),Git 是 "快照存储"(每次 commit 保存整个项目的完整快照);
- 优势:回溯版本、对比差异时速度更快,且能完整恢复任意版本的所有文件。
3. 分支轻量且高效
- Git 的分支本质是 "指向 commit 的指针",创建 / 切换 / 合并分支几乎瞬间完成;
- 支持 "特性分支工作流":一个分支开发一个功能,完成后合并到主分支,避免代码混乱。
4. 数据完整性(哈希校验)
- 所有文件、commit、分支等都通过 SHA-1 哈希值校验,确保数据不被篡改;
- 只要哈希值不变,版本内容就绝对完整,避免传输 / 存储过程中出现数据损坏。
5. 灵活的撤销 / 回溯机制
- 支持多种撤销方式(
checkout/reset/revert/stash),适配不同场景;revert保留历史记录,适合团队协作;reset适合本地调整,兼顾灵活性和安全性。6. 离线工作能力
- 所有核心操作(
commit/branch/log/stash)都可离线完成,仅push/pull需要联网;- 适合通勤、出差等无网络场景下的开发。
四、Git 常用工作流
- 单人开发 :
main分支直接开发 →commit→push;- 多人协作 :
- 主分支
main(仅存放稳定代码);- 开发分支
dev(日常开发);- 功能分支
feature/xxx(每个功能一个分支,完成后合并到dev);- 修复分支
bugfix/xxx(修复线上 bug,合并到main和dev)。
五、Git 实战案例
案例1:
分支是 Git 的杀手锏。想象一下,你正在开发"坦克开火"功能,突然老板让你修一个"菜单打不开"的 Bug。
你不需要回退代码,只需要切一个分支。
但是我目前这个分支功能还没开发好, 假设会影响到项目的运行, 那我也没办法在现在这个节点开一个分支, 我依然需要开发完这个功能或者直接回退吗?
解决方案1:
你的顾虑是对的:如果你现在的代码写了一半,全是报错,这时候直接开一个新分支,新分支里也会全是报错。在那堆烂代码的基础上修 Bug,简直是灾难。
但 Git 早就料到了这种情况。你不需要 开发完,也不需要把代码删掉回退。
解决这个问题有两个核心思路,我为你推荐最经典的一个:"时空冻结大法"(git stash)。
核心逻辑转变
首先纠正一个概念:当你去修"菜单Bug"时,你不是基于你现在"写烂了的坦克代码"去修,而是要基于**"昨天那个正常的版本(Main/master分支)"**去修。
所以流程是:
把手头写烂的代码**"冻结"**起来(藏到一个临时抽屉里)。
你的工作区瞬间变干净,回到上次提交的状态。
切回 Main 分支(它是干净的)。
基于 Main 开一个 Fix-Menu 分支去修 Bug。
修完回来,把冻结的代码**"解冻"**,继续写。
使用 git stash
假设你现在在 feature-tank 分支,代码写了一半,编译报错中。
第一步:冻结现场
输入这个命令:
git stash发生了什么?
你会发现你刚才写的那堆乱七八糟的代码凭空消失了 !你的文件变回了上次 commit 时的干净模样,项目又可以运行了。
别慌,代码没丢,是被 Git 存到一个特殊的"堆栈"里了。
第二步:切换回主基地
现在你的工作区是干净的,可以安全切回主分支了:
git checkout main第三步:去修 Bug
基于干净的主分支,创建一个修 Bug 的分支:
git checkout -b fix-menu然后你去改代码,修好bug,提交:
bashgit add . git commit -m "修好了菜单Bug"最后合并回主分支(这一步看你们团队流程,如果是个人开发就直接合):
git checkout main git merge fix-menu第四步:回到战场(解冻)
Bug 修完了,你要回去继续写你的坦克。
先切回你的开发分支:
git checkout feature-tank然后输入"解冻"命令:
git stash pop发生了什么?
砰!你之前写了一半的、报错的那堆代码又回来了 ,进度条完美衔接。你可以继续接着写,就像中间没被打断过一样。
而且:
解冻出来的,完全是你原来那一堆"写了一半、报错"的旧代码。
而且,那个菜单 Bug 在你当前的这个分支里,依然没有被修复。
那我想要那个修复的 Bug 怎么办?
既然你在主分支(Main)已经修好了 Bug,你肯定希望你的开发分支(Feature-Tank)也能享受到这个修复,对吧?
你需要做一个动作:"同步"(Merge 或 Rebase)。
正确的操作流程:
解冻代码:
git stash pop # 你的烂代码回来了先把你手头的烂代码提交了(WIP 提交法):
git add . git commit -m "WIP: 坦克写了一半,先存一下,准备合并主分支修复"
- 因为如果不提交,Git 可能不准你合并别的分支,怕覆盖你的修改。
把主分支的修复"吸"过来:
# 确保我在 feature-tank 分支 git merge main这句话的意思是:"Git,请把 Main 分支里最新的改动(包括那个修复的菜单 Bug),合并到我当前的分支里来。"
这只是 Git 自动打开了一个叫 Vim 的文本编辑器,让你确认或修改这次合并的"提交备注"(Commit Message)。
因为你正在把主分支的代码合并进子分支,Git 需要生成一个新的**"合并节点"(Merge Commit)**来记录这件事,而每一个 Commit 都必须有备注,所以它弹出来问你:"默认备注是这个,你要改吗?"
🚀 怎么退出去?(最重要的一步)
你需要按以下步骤操作(请确保输入法是英文模式):
按一下键盘左上角的 Esc 键(确保退出了编辑模式)。
输入Shfit+冒号 :(你会看到屏幕最左下角出现了一个冒号)。
输入字母 wq(w 意思是 Write/保存,q 意思是 Quit/退出)。
按回车键 Enter。
大功告成 :
现在,你的代码里既有你写了一半的坦克功能,也拥有了修好的菜单。
补充一个情况:如果我一开始不在分支怎么办?我的写一半的代码不是在分支里写的, 而是直接在main主分支写的;
不用担心,这种情况非常常见!这就是所谓的**"哎呀,我忘切分支了"**事故。
首先告诉你一个事实:即使你觉得自己没开分支,你其实也在分支上。
通常情况下,你默认就在 main (或 master) 分支上。
这反而更危险!因为 main 分支应该是最干净、随时能运行的版本,结果你现在把烂代码直接写在 main 上了。
但别怕,Git 的 stash 有一个隐藏的神奇功能 :它可以把"冻结"的代码,"解冻"到另一个全新的分支去!
我们可以利用这个特性,把你写错位置的代码,"乾坤大挪移" 到它该去的地方。
拯救流程:把烂代码从 Main 上移走
假设场景:
你一直待在 main 分支(虽然你可能没意识到)。
你在 main 上写了一半的坦克代码,全是报错。
你需要修菜单 Bug。
第一步:冻结现场 (在 main 上)
虽然你在 main 上,但没关系,直接冻结:
git stash发生了什么?
你的 main 分支瞬间变得干干净净(回到了上次提交的状态)。
你写了一半的坦克代码被藏进抽屉了。
第二步:去修 Bug (在 main 上操作)
现在 main 是干净的了,你可以按照标准流程修 Bug:
# 1. 为了安全,还是开个分支修 Bug 吧 git checkout -b fix-menu # 2. ...修代码... git add . git commit -m "修好菜单" # 3. 回到 main 合并 git checkout main git merge fix-menu此时,你的 main 分支是最新的、修好 Bug 的、且能正常运行的。
第三步:关键来了!创建这一半代码原本该去的分支
你这时候不能直接解冻, 因为之前写的代码是在main上的, 解冻会把你的原来的bug代码也释放在main分支, 那就麻烦了, 我后续会详细解释;
你之前那堆坦克代码不应该在 main 上,它应该在 feature-tank 上。
所以,现在我们来创建这个迟到的分支:
bash# 基于最新的 main,创建并前往一个新分支 git checkout -b feature-tank第四步:解冻 (移花接木)
现在你已经站在了 feature-tank 分支上,这时候把刚才在 main 上冻结的代码拿出来:
git stash pop发生了什么?
Git 把你刚才在 main 上写的、冻结的那堆代码,释放到了现在的 feature-tank 分支上。
完美结局:你的 main 分支保持了干净(只包含 Bug 修复),而你写了一半的烂代码成功转移到了 feature-tank 分支,你可以继续安心写了。
总结:Git 的一个重要特性
未提交的修改(工作区/暂存区),是不绑定分支的。
这意味着:
你在 A 分支写了行代码(没 commit)。
你 stash 存起来。
你切到 B 分支。
你 stash pop。
这行代码就跑到 B 分支来了。
所以,如果你发现自己忘切分支,直接在 main 上写了代码,通用的补救公式是:
git stash (先藏起来,把 main 弄干净)
git checkout -b new-branch (去建立你本该建立的分支)
git stash pop (把藏起来的代码吐到这个新分支里)
搞定!就像你一开始就写对了分支一样。
如果第三步我不创建分支, 我直接git stash pop, 会不会导致我辛苦修的bug被解冻的原来的代码覆盖?
这是一个非常好的问题!你在担心 Git 会不会像"覆盖文件"那样,简单粗暴地用旧代码替换新代码。
简短回答:
不会"覆盖"消失 ,你的 Bug 修复依然存在。但是,你会面临两个结果:要么两份代码混在一起(主分支再次变脏) ,要么发生冲突(Conflict)。
我们分两种情况来推演发生了什么:
情况一:你修 Bug 的文件,和写烂代码的文件,不是同一个
烂代码(Stash里):改了 Tank.cpp。
修 Bug(Main上):改了 Menu.cpp。
当你执行 git stash pop 时:
Git 会把 Tank.cpp 的修改放回来。
Git 完全不动 Menu.cpp(保留你修好的状态)。
结果:
你的代码里同时拥有了"写烂的坦克"和"修好的菜单"。
坏消息:你的 main 分支又变脏了,又跑不起来了(因为烂代码回来了)。
好消息:你辛苦修的 Bug 还在。
情况二:你修 Bug 的文件,和写烂代码的文件,是同一个!
烂代码(Stash里):改了 Tank.cpp 的第 10 行。
修 Bug(Main上):也改了 Tank.cpp 的第 10 行。
当你执行 git stash pop 时:
Git 会立刻尖叫停下,报出 冲突(Conflict)。
它会告诉你:
"大哥,你存起来的代码说第 10 行要改成 A,但现在的代码里第 10 行已经是 B 了。我不知道听谁的,你自己看着办吧。"
结果:
Git 绝对不会自动覆盖你的 Bug 修复。
它会把两段代码都写在文件里(用 <<<< 和 >>>> 标记),让你手动选择保留哪一个。
这里的核心风险是什么?
虽然你的 Bug 修复不会丢,但我不建议你这么做(直接在 main 上 pop)。
原因有二:
污染主分支(最重要的原因)
main 分支应该是神圣的、随时可以运行的。
你刚修好 Bug,main 本来是绿色的(健康的)。
你一 pop,烂代码回来了,main 瞬间变红了(报错了)。
如果你这时候不小心手抖执行了 git push,你队友拉下来的代码就全都报错了,你会成为团队里的"罪人"。
逻辑混乱
Git 的设计哲学是:烂代码去分支,好代码留主干。
把烂代码解冻回主干,不仅违背了这个原则,还会让你下次想修新 Bug 时,又得再 stash 一次,非常麻烦。
假如我已经手快 pop 了,怎么补救?
如果你不小心在 main 上执行了 git stash pop,发现烂代码污染了主分支,别慌!你可以原地起飞。
补救招数:
即使烂代码已经到了 main 的工作区里,只要你还没 commit,你依然可以现在立刻创建分支:
bash# 1. 我发现我在 main 上 pop 了,这不对! # 2. 此时烂代码在工作区里 # 3. 带着这些烂代码,直接创建一个新分支并跳过去 git checkout -b feature-tank # 4. 现在烂代码跟着你来到了 feature-tank 分支 # 5. 提交它们 git add . git commit -m "WIP: 坦克代码"发生了什么?
Git 允许你带着"未提交的修改"切换/创建分支。
所以,你其实是把这堆烂代码,从 main 上"驮"到了 feature-tank 上 ,然后才提交。
这样,你的 main 分支依然保持着"修好Bug"的干净状态,而烂代码也安全地落到了新分支里。
总结
git stash pop 是合并,不是覆盖。
它会把旧代码和新修复混合在一起。
但这会让你的主分支变脏。
最佳策略:Pop 之前,务必先切到一个新分支 (git checkout -b new-branch),把垃圾倒在垃圾桶里,不要倒在客厅地板上。
案例2:
创建分支的时候可以加注释吗?不然我怎么知道这个分支是干嘛的
直接回答你:
Git 不直接支持像 git commit -m "注释" 那样给分支加一个简单的单行注释。
Git 的设计哲学是:分支名本身就应该是注释。
但是,如果你觉得名字太短写不下,有 3 种方法 可以解决你的问题:
方法一:使用"行业标准"的分支命名法(最推荐 ⭐️)
既然分支名就是注释,那我们就把名字起得有规律、能读懂 。
在专业的开发团队中,我们通常使用 类型/功能描述 的格式。
常用的前缀:
feat/... (Feature): 开发新功能
例子:feat/tank-fire (开发坦克开火功能)
例子:feat/double-jump (开发二段跳)
fix/... (Fix): 修 Bug
例子:fix/menu-crash (修复菜单崩溃)
例子:fix/tank-collision (修复坦克穿模)
docs/... (Documentation): 只改了文档
refactor/... (Refactoring): 代码重构(功能没变,改了代码结构)
这样起名的好处:
当你输入 git branch 时,一目了然:
bashfeat/tank-movement fix/ui-typo * main你一看就知道 feat/tank-movement 是干嘛的,不需要额外注释。
方法二:给分支写"描述说明书"(冷门功能)
Git 其实隐藏了一个功能,可以给分支写长篇大论的描述,但很少人知道。
切换到你想写注释的分支。
输入命令:
bashgit branch --edit-description这时会弹出一个编辑器(类似你提交代码时的界面),你可以在里面随便写多少行注释都可以。写完保存退出。
怎么查看这个注释?
输入这个命令:
git config branch.<分支名>.description
- 缺点 :这个注释通常只保存在你的本地电脑上,推送到远程服务器(如 GitHub)时,别人是看不到的。所以这个功能比较鸡肋,只有你自己看。
方法三:利用第一条 Commit (实战技巧)
如果你觉得名字太短,描述不清,你可以在创建分支后的第一次提交里写清楚。
创建分支 git checkout -b feat/complex-ai
改动一点点东西(或者创建一个空的 commit)。
提交:
codeBash
git commit --allow-empty -m "初始化:开始开发AI功能,计划包含寻路算法和攻击逻辑"以后你查看 Log 时,第一条就很清楚这个分支的起源和目的是什么






