Skip to content

历史版本回滚与Git 忽略文件(Day5) ​

本页对应 Day05_Git,共 6 段视频,约 2 小时 10 分。

学习目标 ​

  1. 能使用 git log、git log --oneline、git reflog 查看提交历史与被回滚记录,并使用 git reset --hard <commit-id> 完成指定版本回滚与恢复。
  2. 能创建并配置 .gitignore,按文件名、目录、通配符、取反等规则忽略文件,并处理已提交文件需先 git rm --cached 再提交的场景。
  3. 能使用 git branch、git switch/git checkout、git switch -c 完成分支创建、切换、重命名和删除,并验证当前分支与分支列表。
  4. 能先提交工作区修改,再在目标分支上使用 git merge 合并开发分支,并说明合并提交的产生过程。
  5. 能识别同一文件被不同分支修改导致的合并冲突,使用编辑器或 VS Code 解决冲突标记,并重新 add、commit 完成冲突解决。
  6. 能按“功能分支→开发分支→主分支”的流程演示合并,并说明主分支不直接开发、冲突在开发分支解决。

知识点 ​

回顾:暂存区、版本库与常用命令 ​

为什么需要:Git 的核心是先把修改放进暂存区,再提交到版本库;不理解这两层,就容易把“改了文件”误认为“已经提交”。

怎么想:工作区是正在编辑的文件;git add 把修改放入暂存区;git commit 才生成版本记录。git status 用来看工作区和暂存区状态。

怎么做:

bash
git config --global user.name "你的名字"
git config --global user.email "你的邮箱"
git init
git add -A
git commit -m "项目初始化"
git status
git diff
git diff --cached

撤销未暂存修改可用 git restore .;已暂存则先 git restore --staged .,再 git restore .。转写稿中演示了误删文件后恢复:先看 git status,红色表示未暂存,恢复后文件回来。

易错点:.git 是版本库,不要删、不要改;git add -A 前先确认没有不想提交的文件;git restore 对新增文件不会自动删除,需要手动处理。

历史版本回滚:log、reflog 与 reset ​

为什么需要:提交错了要能回到过去;回滚错了还要能“回到未来”。

怎么想:git log 看当前提交历史;git log --oneline 更清晰;git reset --hard <commitID> 直接重置工作区、暂存区和 HEAD。回滚后 git log 看不到被回滚掉的提交,但 git reflog 有操作记录。

怎么做:

bash
git log
git log --oneline
git reset --hard <commitID>
git reflog

转写稿演示:把首页改坏并提交,git log 找到“修改首页”的提交号,执行 git reset --hard 后代码恢复;再后悔时用 git reflog 找到“首页重新设置”的提交号,再 reset --hard 回来。提交号不必全写,前七位通常够用。

易错点:--hard 会丢弃当前未提交修改;回滚后不要以为历史真没了,先查 reflog;复制提交号时别多复制。

忽略文件:.gitignore ​

为什么需要:操作系统生成文件、编译中间文件、个人配置文件不应提交,否则会污染仓库,甚至泄露账号密码等敏感信息。

怎么想:在项目根目录建 .gitignore,写规则。只写文件名表示任意目录下同名文件都忽略;前面加 / 表示只忽略项目根目录;*.js 表示所有 .js 文件;!01.js 表示排除。目录规则可用 / 结尾。

怎么做:

bash
# 新建 .gitignore
/config.json
*.js
!01.js

如果文件已经提交过,再写忽略不会生效,需要先从版本库删除缓存:

bash
git rm --cached index.css
git add -A
git commit -m "修改忽略文件"

易错点:.gitignore 本身通常要提交;已提交文件必须先 git rm --cached;规则写错会导致该忽略的没忽略,或误忽略源码。

分支:创建、切换、删除、重命名 ​

为什么需要:主分支要保持可运行、可发布;新功能应在分支开发,避免半成品影响主线。

怎么想:先提交当前工作区,再操作分支。git branch 查看分支,git switch 切换,git switch -c 创建并切换。

怎么做:

bash
git branch
git branch dev
git switch dev
git switch -c shopcart
git branch -m dev develop
git branch -D shopcart

转写稿强调:切到主分支后,开发分支上的新文件不会出现;切回开发分支又能看到。分支创建时会把已有提交带过来。

易错点:分支操作前先 git status 确认已提交;不能删除当前所在分支;删除用 -D 要确认分支已合并或不再需要。

合并分支与冲突解决 ​

为什么需要:功能开发完要合并回开发分支,测试后再合并到主分支;多人或多分支改同一文件时会产生冲突。

怎么想:把 A 合并到 B,先切到 B,再 git merge A。先合并的分支通常没事,后合并且改了同一文件的分支容易冲突。冲突时 Git 会在文件里插入特殊标记,需要程序员手动决定保留哪边。

怎么做:

bash
git switch dev
git merge shopcart
git merge order

冲突后打开文件,删除 <<<<<<<、=======、>>>>>>> 等标记,保留需要的代码,然后:

bash
git add -A
git commit -m "解决冲突"

转写稿中先合并购物车无冲突;再合并订单时,index.html 冲突,VS Code 提供“采用当前更改/传入更改/保留双方更改”,也可手动删标记。

易错点:冲突解决后必须重新 add 和 commit;不要保留冲突标记;合并完成后可删除临时功能分支,下次开发再从最新开发分支新建。

本节目标与命令总览 ​

这一节是对 Git 入门操作的回顾:从安装后的初始化配置,到仓库初始化、暂存、提交、查看状态与差异,再到未提交前的两种撤销。转写稿里反复提到的“get/ge”应纠正为 Git,“展存区/白水库”应纠正为 暂存区/版本库。学习时不要只背命令,而要抓住一条主线:工作区修改 → 暂存区 → 版本库。下面按笔记中的 8 个知识点逐一梳理。

1. 初始化配置 ​

为什么需要:提交时 Git 要记录“是谁提交的”,所以安装完成后只需配置一次用户名和邮箱。
怎么想:这是身份标记,不要求注册;邮箱填自己常用邮箱即可。
怎么做:

bash
git config --global user.name '用户名'
git config --global user.email '用户名'

易错点:笔记里邮箱也写成“用户名”,实际应填邮箱;--global 表示当前用户全局配置,不必每个项目重复配置。

2. 仓库初始化 ​

为什么需要:每个项目要独立成为一个 Git 仓库,才能被 Git 管理。
怎么想:进入项目目录后执行一次即可,会生成隐藏目录 .git,暂存区、版本库等都在其中。
怎么做:

bash
git init

易错点:.git 不要删除、不要随意修改;删掉后这个项目的版本管理信息就没了。转写稿演示中先建 01-project,再在项目目录里执行 git init,这个顺序要清楚。

3. 添加到暂存区 ​

为什么需要:工作区改完不会自动进入下一次提交,要先告诉 Git“哪些修改要提交”。
怎么想:可以按文件名精确添加,也可以 -A 一次添加全部变化。转写稿里讲师更推荐 git add -A,简单直接。
怎么做:

bash
git add 文件名
git add -A

易错点:转写稿演示 git add -A 时出现换行符转换警告,这是 Windows 的 CRLF 与 Unix 的 LF 差异提示,不影响开发,可先忽略;但不要因此误以为添加失败。

4. 提交到版本库 ​

为什么需要:暂存区只是“准备提交”,真正形成一次历史版本要靠 commit。
怎么想:每次提交写清楚提交信息,方便以后通过 git log 找到这次改了什么。
怎么做:

bash
git commit -m '提交信息'
git commit -am '提交信息'

易错点:-am 是“添加并提交”的合并写法,初学建议像转写稿那样把 add 和 commit 分开做,便于观察暂存区状态;提交信息要写“项目初始化”“修改首页”这类可读说明。

5. 查看状态 ​

为什么需要:操作前先知道工作区、暂存区有没有变化,避免盲目提交。
怎么想:git status 是“当前局势面板”:红色通常表示工作区有修改,已暂存的文件也会被列出。
怎么做:

bash
git status

易错点:转写稿演示新增文件、修改首页、删除图片后都先看 status;不要只看颜色,要读懂“未跟踪/已修改/已暂存”等提示。

6. 查看具体修改的信息 ​

为什么需要:status 只告诉你有变化,diff 才能看具体改了什么。
怎么想:工作区与上次提交比,用 git diff;暂存区与上次提交比,用 git diff --cached。
怎么做:

bash
git diff
git diff --cached

易错点:已经 add 的修改,普通 git diff 可能看不到,要用 --cached;日常先看 status,需要细看再 diff。

7. 撤销工作区的修改 ​

为什么需要:改错了但还没加入暂存区,希望恢复到上次提交的样子。
怎么想:这是“未 add 前”的后悔药,针对工作区。
怎么做:

bash
git restore 文件名
git restore .

易错点:转写稿明确提醒:新增文件不会被 restore 撤销,需要手动删除;已删除或已修改的旧文件通常可以恢复。演示中误删首页和图片后,git status 显示红色,执行 git restore . 后文件恢复。

8. 撤销暂存区 ​

为什么需要:已经 git add 了,但不想让这些修改进入下一次提交,需要先退出暂存区。
怎么想:分两步理解:先 --staged 撤销暂存,再按需恢复工作区。
怎么做:

bash
git restore --staged 文件名
git restore --staged .

易错点:转写稿演示“删除图片并已 add”的情况:先执行 git restore --staged . 把删除撤出暂存区,此时工作区仍是删除状态;再执行 git restore . 才把工作区文件恢复。不要以为一条 --staged 就能同时恢复工作区。

先分清:撤销未提交修改与回滚已提交版本 ​

为什么需要:改错文件时,错误可能还没提交,也可能已经提交。两种情况处理方式不同,混用命令容易把正常代码也丢掉。
怎么想:先看 git status。如果文件是红色、还没进暂存区,属于“未提交修改”,用 restore 撤销;如果已经 commit,才进入“历史版本回滚”,用 reset。
怎么做:转写稿里演示了误删文件后的恢复:

bash
git status
git restore index.html
# 如果已经 add 到暂存区
git restore --staged .
git restore .

git restore 文件名 恢复工作区;git restore --staged . 只撤销暂存,工作区修改还在,所以通常还要再 git restore .。
易错点:restore 不会撤销新增的未跟踪文件;新增文件要手动删除。--staged 不是“连工作区一起恢复”,别只执行一半就以为完成了。

查看历史提交:先找到要回到的 commit ID ​

为什么需要:回滚必须指定“回到哪一次提交”,不能凭感觉。
怎么想:用 git log 看提交记录,重点看 commit ID、作者、日期和提交信息。提交很多时用 --oneline 简化,用 -n 限制条数。
怎么做:

bash
git log
git log -3
git log --oneline

转写稿中误改首页并提交后,先执行 git log,找到“修改首页”之前的那次提交,再复制它的 commit ID。
易错点:git log 默认只显示当前分支可达的提交;被 reset 掉的提交可能看不到,后面要用 reflog。commit ID 不用手抄全,复制准确即可。

回滚到指定版本:git reset --hard ​

为什么需要:已经提交的错误版本会影响项目运行,需要让工作区、暂存区和当前分支一起回到过去某个提交。
怎么想:reset --hard 是“直接重置”,不是只改暂存区。它会把 HEAD、暂存区、工作区都指向目标版本,所以执行前必须确认没有未提交的重要修改。
怎么做:

bash
git reset --hard <commit ID>

转写稿演示:把首页改成错误内容并 commit 后,执行 git reset --hard 加上目标 commit ID,文件直接恢复,git status 变干净,git log 里后面的错误提交也不见了。
易错点:--hard 会丢弃未提交修改;执行前先 git status。不要删 .git 目录,版本库信息都在里面。回滚后 git log 看不到被回滚的提交,不代表它立刻消失,可用 reflog 找回。

后悔回滚:用 git reflog 找回被回滚的提交 ​

为什么需要:回滚错了,想回到“回滚前”的版本,但 git log 已经看不到那次提交。
怎么想:reflog 记录 HEAD 的操作历史,包括提交、回滚等动作。先 git reflog 找到旧 commit ID,再 reset --hard 回去。
怎么做:

bash
git reflog
git reset --hard <旧commit ID>

转写稿中先回滚到“修改首页”,又后悔想回到“首页重新设置”,就是通过 reflog 找到被回滚掉的提交 ID,再执行 reset --hard。
易错点:reflog 是本地操作记录,不是远程共享历史;commit ID 通常写前 7 位即可,但要以能唯一识别为准。执行 --hard 前仍要确认目标。

快速回退上一版:HEAD^ 的用法 ​

为什么需要:只回退一两个版本时,复制 commit ID 麻烦,可以用相对位置。
怎么想:HEAD 指当前版本,HEAD^ 指上一个版本,HEAD^^ 指上上个版本。
怎么做:

bash
git reset --hard HEAD^
git reset --hard HEAD^^

转写稿演示了 git reset --hard HEAD^ 回到“修改首页”的上一个状态。
易错点:回退多个版本容易数错;如果目标提交已经被 reset 掉,正常 HEAD^ 不一定能找到,仍要用 reflog。--hard 的危险性不变。

小结:回滚的底层逻辑 ​

先 git status 分清是否已提交;已提交后用 git log 找目标 commit ID;确认后 git reset --hard 回滚;回滚错了用 git reflog 找旧 ID 再回去。所谓“版本穿梭/穿越”只是形象说法,核心是 commit ID 和 HEAD 的移动。操作前尽量提交或备份,避免 --hard 丢掉未提交内容。

为什么需要 .gitignore ​

这一节只聚焦“Git 忽略文件”。转写稿里先问了一个问题:哪些文件“我不想被 Git 管理”?答案是三类。

第一类是操作系统或编辑器自动生成的文件,比如苹果系统每个目录下可能出现的 .DS_Store,以及某些代码编辑工具生成的隐藏文件。它们不是项目源码,提交上去没意义,还会随着环境变化自动更新。

第二类是编译生成的中间文件、可执行文件。转写稿举了 Java 的 .class,也讲了前端:less 不能直接运行,要编译成 CSS;模块化 JS 也要经过编译处理,浏览器才能直接运行。团队同步代码时,只同步程序员写的源码,编译结果各自在本地生成即可。所以这类文件应被忽略。

第三类是配置文件。每个人电脑上的开发环境不同,数据库账号、密码、端口号往往不一样。转写稿还提到安全风险:如果把密钥、账号密码提交到公开仓库,可能被有心之人利用。因此配置文件也应忽略。

怎么想?判断标准很简单:自动生成的、可重新编译的、个人环境相关的、含敏感信息的,不提交;程序员手写的源码、项目说明、.gitignore 本身,要提交。

怎么做:创建并放置 .gitignore ​

忽略文件的名字是固定的:.gitignore,不能随便改名。创建方式很朴素:在项目里新建文本文档,把文件名改成 .gitignore,注意把后面的 .txt 也删掉。转写稿提醒,Windows 7 对“点开头”的文件名可能不太好改,需要额外手段;Win10/Win11 一般直接改即可。

放在哪里?.gitignore 可以放在项目任意目录,但作用范围只限该目录。一般放在项目根目录,这样整个项目都受它影响。转写稿里项目根目录是 01-project,把 .gitignore 放在这里,整个仓库都能按规则忽略。

还要注意:.gitignore 本身通常要提交,因为它描述的是团队共同的忽略规则;不要把它自己忽略掉。

怎么做:写规则并验证 ​

转写稿里先建了一个 config.json,内容类似:

json
{
  "dbAddress": "数据库地址",
  "dbUser": "root",
  "dbPassword": "123456",
  "dbPort": 3306,
  "dbName": "小乐"
}

然后在 .gitignore 里写规则。常见写法如下:

gitignore
# 忽略根目录下的配置文件
/config.json

# 忽略所有 .js 文件
*.js

# 但 01.js 不忽略
!01.js

# 忽略 dist 目录及其下文件
dist/

写完后用 git status 验证:被忽略的文件不再出现在未跟踪列表里;VS Code 里被忽略文件常显示为深灰色。转写稿中先只写 config.json,发现项目里所有同名文件都被忽略;后来在前面加 /,才变成“只忽略项目根目录下的 config.json”。这就是调试过程:只写文件名,范围是全局;前加斜杠,才限定根目录。

# 开头是注释,空格没有实际含义。*.js 表示所有以 .js 结尾的文件;!01.js 是排除,但排除规则要写在范围大的规则后面。目录末尾加斜杠表示忽略目录,比如 dist/。

易错点:规则格式与已提交文件 ​

转写稿里反复试过一个坑:规则之间用换行分隔,不要随手在行尾加分号。分号不是 Git 忽略规则的换行符,加了以后可能被当成匹配内容,导致规则看起来“没生效”。调试时应该先看 .gitignore 是否写对,再用 git status 验证。

更大的坑是:文件已经提交到版本库后,再写进 .gitignore 仍然会显示变化。因为版本库里已经有它了。转写稿里的处理办法是:先把文件从版本库移除,但保留工作区文件,然后提交这次修改。命令是:

bash
git rm --cached index.css
git commit -m "修改忽略文件"

注意 git rm --cached 删的是版本库里的记录,不是工作区文件;执行时路径要对,转写稿里第一次报错就是因为文件在子目录,命令里的路径没对上。处理完再 git status 验证,之后修改该文件就不会再被 Git 跟踪了。

最后记住:.gitignore 不是“删除文件”,而是告诉 Git“以后别管它”。已经提交过的文件,必须先移出版本库,再让忽略规则生效。

分支为什么需要 ​

分支不是 Git 的专利,但 Git 的分支非常常用。转写稿里反复强调一个场景:线上项目本来能正常运行,你继续开发时会出现“半成品”,甚至把原代码改烂。为什么需要:不能让未完成代码影响稳定版本;多人协作时也不应所有人挤在同一条线上。怎么想:主分支保存经过测试、可发布的完整代码;开发分支承接日常开发;功能模块再基于开发分支开子分支,如购物车、订单。怎么做:先在开发分支开发,测试通过后合并回开发分支,再由开发分支合并到主分支。易错点:不要在主分支提交半成品;执行分支操作前一定先提交工作区修改,否则 Git 会报错或让你先处理未提交内容。

操作前先看状态和历史 ​

为什么需要:分支切换、合并都建立在“当前修改已提交”的基础上。怎么想:先 git status 看工作区是否干净,再用 git log --oneline 看提交记录和提交号。怎么做:

bash
git status
git log --oneline
git reflog

转写稿中“get log 杠杠 online line”应纠正为 git log --oneline;reflog 能查看被回滚掉的操作记录。回滚可用:

bash
git reset --hard <commitID>

提交号不必写全,前七位通常足够。易错点:reset --hard 会直接改工作区;回滚后 git log 可能看不到被回滚的提交,但 git reflog 仍可找回。

查看、创建、切换、重命名、删除分支 ​

为什么需要:分支名就是工作线的名字,必须知道当前在哪条线上。怎么想:git branch 看列表,星号表示当前分支;创建后不会自动切换。怎么做:

bash
git branch
git branch dev
git switch dev
git branch -m dev develop
git branch -d develop

一步创建并切换:

bash
git switch -c dev

旧命令 git checkout 也能切换,转写稿说两个都行。易错点:不能删除当前所在分支,必须先 git switch master 再删;分支名可自定义,但团队常用 dev、feature 等语义名。

合并分支 ​

为什么需要:功能分支开发完成后要回到开发分支或主分支。怎么想:把 A 合并到 B,就先切换到 B,再 git merge A。怎么做:

bash
git switch dev
git merge shopcart
git merge order

转写稿中“word质/墨汁”应纠正为 merge。合并本身可能多出一个提交,尤其是第二个分支合并进来时,Git 会自动生成合并提交信息。易错点:先合并的分支通常没事,后合并且改了同一文件的分支容易冲突;合并前先确认当前分支。

冲突怎么产生、怎么解决 ​

为什么需要:两个分支改了同一个文件,Git 不知道保留谁。怎么想:先合并的没事,后合并的倒霉;冲突不是“合并失败就什么都没做”,Git 往往已把能自动合并的内容拿过来,并在冲突文件里插入特殊标记。怎么做:打开冲突文件,会看到类似:

text
<<<<<<< HEAD
购物车内容
=======
订单内容
>>>>>>> order

程序员手动决定保留当前、保留传入或双方都保留;VS Code 可点“采用当前更改/采用传入更改/保留双方更改”。处理完保存,再:

bash
git add -A
git commit -m "解决冲突"

易错点:冲突标记 <<<<<<<、=======、>>>>>>> 必须删干净,否则代码不能正常运行;解决后仍要提交。

忽略文件与已提交文件 ​

为什么需要:系统自动生成文件、编译中间文件、个人配置文件不应提交。怎么想:项目根目录建 .gitignore,规则按行写。怎么做:

text
# 忽略配置文件
/config.json
*.js
!01.js
build/

/config.json 只忽略根目录;*.js 忽略所有 JS;!01.js 表示排除;build/ 表示目录。易错点:转写稿中“加分号”是调试口误,.gitignore 通常每行一条,不需要分号。若文件已提交过,再写忽略不生效,需要:

bash
git rm --cached index.css
git commit -m "修改忽略文件"

删除的是版本库记录,不是工作区文件;操作后要再提交。分支开发中把这些准备动作做干净,再合并,冲突会少很多。

说明:本节转写稿实际讲的是本地 Git 的回顾、版本回滚、忽略文件与分支操作,未出现 push/pull/remote 等远程仓库内容,因此以下按稿中真实内容整理,不补写远程仓库步骤。

初始化、暂存与提交:先把本地版本库跑起来 ​

为什么需要:Git 安装后要先做一次身份标记,项目目录要初始化成版本库,后续提交才知道“是谁提交的”。怎么想:配置只做一次;初始化每个项目做一次;日常开发就是“改文件→看状态→加暂存区→提交”。怎么做:

bash
git config --global user.name "小乐"
git config --global user.email "xiaole@example.com"
git init
git add -A
git commit -m "项目初始化"

易错点:.git 是版本库,不要删、不要乱改;git add -A 是把所有改动加入暂存区,提交前仍要用 git status 确认。转写稿里 git add -A 出现换行符警告,原因是 Windows 的 CRLF 与类 Unix 的 LF 转换,一般不影响开发,可先忽略。

状态查看与撤销:先分清工作区、暂存区、版本库 ​

为什么需要:改完文件后要知道“改了哪些、有没有加到暂存区”,否则容易把半成品提交进去。怎么想:git status 看大局;git diff 看工作区差异;git diff --cached 看暂存区差异。撤销要分两种情况:没 add 用 git restore,已经 add 先 git restore --staged 再恢复工作区。怎么做:

bash
git status
git restore index.html
git restore --staged .
git restore .

易错点:git restore 不会撤销“新增文件”,新增文件要手动删除;已暂存的删除操作要分两步恢复,先撤暂存区,再撤工作区。

历史版本回滚:log 看现在,reflog 找回“回滚掉的提交” ​

为什么需要:提交错了要能回到过去;回滚错了还要能回来。怎么想:git log 只看当前链路上的提交;git reflog 记录操作历史,能看到被回滚掉的那次提交。怎么做:

bash
git log --oneline
git reset --hard <commitID>
git reflog
git reset --hard HEAD~1

易错点:reset --hard 会直接改工作区,执行前先提交或确认;提交号不必写全,前 7 位通常够用;HEAD~1 表示上一个版本,连续回退容易数错,实际更推荐从 reflog 复制提交号。

忽略文件:不该进版本库的,不要提交 ​

为什么需要:系统生成文件、编译产物、本机配置文件不应同步,否则会互相覆盖,甚至泄露数据库账号密码。怎么想:在项目根目录建 .gitignore,规则按行写;只写文件名会忽略所有同名文件,写 /config.json 才只忽略根目录。怎么做:

gitignore
# 忽略配置文件
/config.json
*.js
!01.js

易错点:.gitignore 每行一条,结尾不要加分号;已经提交过的文件,后加忽略规则不会自动生效,要先从版本库移除再提交:

bash
git rm --cached index.css
git commit -m "修改忽略文件"

分支创建、切换、删除、重命名:开发不要直接压在主分支上 ​

为什么需要:主分支要保持可运行、可发布;新功能放分支开发,坏了不影响主线。怎么想:先提交工作区,再建分支;创建并切换可用 switch -c;删除分支前先切走。怎么做:

bash
git branch
git switch -c dev
git branch -M dev devu
git switch master
git branch -d devu

易错点:分支操作前必须提交工作区,否则可能报错;不能删除当前所在分支;git branch -M 是重命名,旧名在前、新名在后。

合并与冲突:先合并的没事,后合并同文件才冲突 ​

为什么需要:功能分支完成后要合回开发分支,测试没问题再合主分支。怎么想:把 A 合到 B,先切到 B,再 git merge A。两个分支改同一文件时,后合并者会冲突。怎么做:

bash
git switch dev
git merge shopcut
git merge order

冲突后文件里会出现:

text
<<<<<<< HEAD
购物车
=======
订单
>>>>>>> order

易错点:冲突标记不是代码,必须手动删掉并决定保留哪边;解决后要 git add -A、git commit -m "解决冲突";合并本身可能多出一次提交,git log --oneline 能看到。

GitFlow 要解决什么问题 ​

为什么需要:项目已经能正常运行、甚至上线后,如果继续在唯一分支上开发,半成品代码会混进稳定代码,线上版本就可能被“开发到一半”的页面拖垮。GitFlow 的核心不是背命令,而是用分支把“稳定发布”和“持续开发”隔开。

怎么想:主分支只放经过测试、可发布的版本;日常开发在开发分支集成;每做一个功能,就从开发分支拉一个功能分支,做完再合并回开发分支;线上紧急 Bug 用热修分支单独处理。转写稿里反复强调:主分支不要直接开发,要保证它是完整、可测试、和线上一致的代码。

怎么做:先记住五个分支的角色:

  • Master 主分支:只保存正式发布的版本。
  • Hotfix 线上代码 Bug 修复分支:开发完后需要合并回 Master 和 Develop。
  • Feature 功能分支:开发某个功能时创建单独分支,开发完毕后再合并到 dev 分支。笔记里的“Feather”是笔误,按转写稿应理解为 Feature。
  • Release 待发布分支:基于 Develop 分支创建,在这个 Release 分支上测试、修改 Bug。
  • Develop 开发分支:开发者都在这个分支上提交代码。

易错点:不要把 Master 当日常开发分支;不要在一个功能分支里塞多个无关功能;主分支默认名可能是 master,也可能是 main,名字可改,但“稳定发布”的职责不能变。

功能分支从创建到合并的推导 ​

为什么需要:一个功能可能开发好几天,中间会提交很多半成品。如果直接在 Develop 上乱改,虽然比改 Master 安全,但多个功能并行时仍容易互相干扰。所以每个功能单独开分支。

怎么想:转写稿里的标准流程是:Develop 与 Master 保持同步;要开发购物车,就基于 Develop 创建 shopcart 分支;要开发订单,就基于 Develop 创建 order 分支;各自开发、各自提交;完成后都合并回 Develop;在 Develop 上测试,没问题再把 Develop 合并到 Master。

怎么做:常用命令如下,注意分支操作前先 git status,确认工作区修改都已提交:

bash
git branch
git switch develop
git switch -c feature/shopcart
# 在功能分支开发并提交
git add -A
git commit -m "完成购物车页面"

git switch develop
git merge feature/shopcart

功能分支合并回 Develop 后,可删除已合并分支:

bash
git branch -d feature/shopcart

易错点:合并方向别搞反——要把功能分支合到 Develop,就先切到 Develop,再 git merge 功能分支。删除分支时不能删除当前所在分支;转写稿里删除失败,就是因为还站在该分支上,先切走再删。合并本身可能多出一次提交,这是正常现象。

合并冲突的调试过程 ​

为什么需要:两个功能分支如果改了同一个文件,Git 不知道怎么保留,就会产生冲突。转写稿的演示很典型:购物车分支和订单分支都改了 index.html,先合并购物车没事,后合并订单时冲突出现。

怎么想:Git 不是“什么都不做”,它会尽量自动合并;失败时会在文件里插入冲突标记,把双方修改都摆出来,让程序员决定保留谁。冲突要在 Develop 上解决,解决完再提交。

怎么做:合并后看状态,打开冲突文件,手动删除特殊符号,保留需要的代码;也可以用 VS Code 的“采用当前更改 / 采用传入更改 / 保留双方更改”。处理完执行:

bash
git status
git add .
git commit -m "解决冲突"

易错点:不要带着冲突标记直接运行项目;不要不看业务逻辑就无脑“保留双方”;冲突解决后必须重新 add、commit。转写稿里“先合并的没事,后合并的倒霉”不是绝对规律,本质是看是否改了同一处内容。

GitFlow 流程中的易错点 ​

为什么需要:GitFlow 看起来分支多,真正容易错的不是命令,而是流程纪律。

怎么想:Master 被动接收 Develop 的合并;功能分支短生命周期,合并后删除;Release 用于发布前测试修 Bug;Hotfix 用于线上紧急修复,并合并回 Master 和 Develop。

怎么做:每次开分支前先提交工作区;合并前先切到目标分支;合并后跑测试;确认稳定再合 Master。

易错点:主分支直接改代码;功能分支长期不合并导致冲突越来越大;分支操作前工作区不干净;删除分支前没合并;把“Feather”当成正式术语。记住:分支是隔离风险的工具,不是越多越好。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

课堂画面 6

代码示例 ​

示例一:index.html(首页,用于分支冲突演示) ​

html
<!-- index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>首页</title>
  <link rel="stylesheet" href="./css/index.css">
</head>
<body>
  <h1>首页</h1>

  <!-- 购物车分支上加的超链接 -->
  <a href="./shopcart.html">购物车</a>

  <!-- 订单分支上加的超链接 -->
  <a href="./order.html">订单</a>

  <script src="./js/index.js"></script>
  <script src="./js/data.js"></script>
</body>
</html>

关键行与易错点

  • <link rel="stylesheet" href="./css/index.css"> 与两个 <script> 的路径,对应转写稿里的项目结构:根目录放 index.html,子目录放 css/、js/、images/。
  • 两个 <a> 是本节冲突演示的核心素材:购物车分支和订单分支都改了 index.html 的同一处,先合并的没事,后合并的必冲突(转写稿原话:“谁后合并谁倒霉”)。
  • 改完页面必须走完整流程再切分支:git status(红字=工作区未提交)→ git add -A → git commit -m '修改首页'。转写稿反复强调:执行任何分支操作之前,先把工作区的修改提交掉,否则报错。
  • 撤销工作区修改用 git restore .;但新增的、从没被跟踪过的文件撤销不掉,只能手动删(转写稿明确说明:“新增的文件还会在”)。
  • 如果已经 git add 进了暂存区,要两步恢复:先 git restore --staged . 撤销暂存,再 git restore . 撤销工作区修改。

示例二:css/index.css 与 js/index.js、js/data.js(分支各自开发的素材) ​

css
/* css/index.css */
* {
  margin: 0;
  padding: 0;
}

h1 {
  font-size: 24px;
}

a {
  margin-right: 10px;
}
javascript
// js/index.js
console.log('index');
javascript
// js/data.js
// 开发分支上“添加数据脚本”那次提交,随便定一个变量
var d;

关键行与易错点

  • margin: 0; 就是转写稿里“保底、margin 二乘 0”的那段样式重置,只是让项目能跑起来,不是重点。
  • console.log('index') 的用途是验证切分支后工作区代码真的变了:切到主分支,data.js 会消失、index.html 里那两行超链接也会消失;切回开发分支又全部回来。
  • var d; 对应“添加一个数据脚本”那次提交,和 index.html 的修改要分成两次提交(转写稿演示时就是两次:修改首页、添加数据脚本),这样 git log --oneline 才能看出开发分支在主分支三条提交之后又追加了两条。
  • 易错点:git log --oneline 只显示前 7 位 commit id,回滚时只写前七位就够了(转写稿:“前七位肯定不重复”)。
  • 易错点:git reset --hard <commitId> 回滚后再想看被回滚掉的提交,git log 已经看不到了,要用 git reflog 找回那次提交的版本号,再 git reset --hard 回去。

示例三:config.json 与 .gitignore(忽略文件演示) ​

json
{
  "dbAddress": "localhost",
  "dbUser": "root",
  "dbPassword": "123456",
  "dbPort": 3306,
  "dbName": "小乐"
}
gitignore
# 忽略配置文件:只忽略项目根目录下的 config.json
/config.json

# 忽略所有以 .js 结尾的文件(不管在哪一层目录)
*.js

# 排除法:上面忽略了所有 .js,唯独 01.js 不忽略
!01.js

# 以 / 结尾表示忽略的是一个「目录」,连目录里的文件一起忽略
dist/

关键行与易错点

  • config.json 里键必须用双引号,字符串值必须双引号,数字(3306)可以直接写,最后一项后面不能加逗号——JSON 只是“长得像 JS 对象”,不是 JS。
  • 忽略文件名固定叫 .gitignore(注意开头那个点,Windows 下要把 .txt 后缀一起删掉),一般放在项目根目录,作用范围就是整个项目;放在 images/ 里就只管 images/。
  • config.json 是典型的“每人电脑不一样”的文件,必须忽略:否则你把数据库密码提交上去,同事一同步就把自己的配置覆盖了;公开仓库上还会泄露密码(转写稿讲的那个真实案例)。
  • /config.json(斜杠在前)只忽略根目录这一个;写成 config.json(不带斜杠)会忽略所有目录下同名文件;写 *.js 同理,全项目通杀。
  • !01.js 这种排除规则必须写在范围大的规则后面,顺序写反就失效。
  • 最关键的坑:已经提交到版本库的文件,再写进 .gitignore 是不生效的。必须先 git rm --cached 文件名(只删版本库、不删工作区),再 git commit -m '修改忽略文件' 提交一次,之后 git status 才不会再看它。
  • 被忽略的文件在 VSCode 里会显示成深灰色,git status 里根本不出现,git add -A 也不会带上它。

常见错误 ​

1. git add -A 弹出一堆 LF will be replaced by CRLF 警告

  • 现象:添加文件时刷出多条换行符转换警告,新手以为操作失败。
  • 原因:Windows 用 CRLF、Unix 用 LF,Git 在入库时自动做换行符转换并提示。
  • 修法:不影响运行,直接忽略继续提交;若团队要求统一,配置 core.autocrlf 或用 .gitattributes 约定。

2. 文件误删后 git restore . 恢复不回来,或恢复了但 status 还显示删除

  • 现象:删掉几个文件后执行一次 restore,文件仍缺失或状态没清干净。
  • 原因:删除操作已经被 git add 进了暂存区,只撤销工作区不够。
  • 修法:先 git restore --staged . 撤销暂存区,再 git restore . 撤销工作区,两步走。

3. git reset --hard 回滚后 git log 里看不到原来的提交,以为代码永久丢失

  • 现象:回滚到旧版本后想再回去,git log 里已经没有那次提交了。
  • 原因:log 只显示当前分支可达的提交,被"甩掉"的提交不在其中,但并未删除。
  • 修法:用 git reflog 找到那次提交的 ID(复制前 7 位即可),再 git reset --hard <id> 回来。

4. .gitignore 写好了,但被忽略的文件仍然出现在 git status 里

  • 现象:规则明明写了,文件还是被跟踪。
  • 原因:该文件早已被提交进版本库,忽略规则只对未跟踪文件生效;或文件名/路径拼错;或规则行尾多加了分号、空格。
  • 修法:git rm --cached 文件名 把它从版本库移除(保留本地文件),再提交一次;同时逐条核对规则拼写。

5. 想只忽略根目录的配置文件,结果子目录下的同名文件也被忽略了

  • 现象:写了 config.json,js/config.json 一并消失。
  • 原因:不带斜杠的文件名规则是"任意层级全匹配"。
  • 修法:写成 /config.json,斜杠代表仓库根目录,只作用于根下。

6. 删除分支时报 Cannot delete branch ... checked out at

  • 现象:git branch -d dev 报错删不掉。
  • 原因:当前正处在该分支上,Git 不允许删除正在使用的分支。
  • 修法:先 git switch master(或其他分支)切走,再执行删除。

7. 合并时看到冲突标记,直接 git commit 或误以为合并彻底失败

  • 现象:文件里出现 <<<<<<<、=======、>>>>>>>,或者 commit 后代码跑不起来。
  • 原因:两个分支改了同一文件的同一处,Git 完成了自动合并但把决策权留给了人;冲突标记本身就是非法代码。
  • 修法:手动(或用 VS Code 的"保留双方更改")删掉所有标记,留下想要的内容 → git add → git commit,提交信息写明"解决冲突"。

8. 复制 commit ID 时多带了字符,reset 报错

  • 现象:粘贴后执行命令提示找不到对象。
  • 原因:从 git log 的多行输出里连拖带选,复制范围不准。
  • 修法:用 git log --oneline 只取前 7 位;粘贴后回看一遍再回车。

练习 ​

练习 1:后悔药闭环 在你已有的项目里:① 把 index.html 改成一句乱码并提交;② 用 git log --oneline 记录版本号;③ reset --hard 回到上一个版本,确认页面恢复;④ 再回到那个"乱码版本";⑤ 用 reflog 打印全过程并截图。 要求:写出每一步的命令,并回答"为什么第 ③ 步之后 git log 里少了一条"。

练习 2:忽略规则三连验证 在项目根目录建 config.json,再在 js/ 下建一个同名 config.json,另外建 01.js、02.js。 分别测试三种写法下 git status 的差异:config.json、/config.json、*.js;然后再补一条 !01.js,观察结果。 要求:整理成一张"规则 → 影响范围"的对照表,并说明哪种写法最贴近你真实项目需要。

练习 3:忽略已入库的文件 故意先把 css/index.css 提交进版本库,然后再把它写进 .gitignore。 要求:让它真正从 git status 中消失,写出完整命令序列,并说明 git rm --cached 和 git rm 的区别。

练习 4:双分支并行开发 在 master 基础上建 dev,从 dev 再建 shopcart 和 order 两个分支;分别在各自分支新建一个页面文件并各提交一次;依次合并进 dev,合并完成后删除这两个分支,最后把 dev 合并到 master。 要求:合并前后各执行一次 git log --oneline,用提交记录说明"哪几次提交是从哪来的"。

练习 5:制造并解决冲突 基于 dev 重新创建 shopcart 和 order 两个分支,让它们修改同一个文件的同一段内容;先把 shopcart 合进 dev(应无冲突),再合 order。 要求:截图冲突标记,用两种方式各解决一次(VS Code 面板 / 手动删标记),最后 git status 必须干净,并在提交信息中体现"解决冲突"。

原视频对照 ​

#视频时长
1回顾13:03
2历史版本回滚19:22
3忽略文件37:39
4分支操作-创建切换删除重命名19:16
5分支操作-合并分支07:47
6分支操作-解决合并分支时的冲突32:51

小结 ​

  1. 只要提交过,就几乎不会真的丢东西:Git 记录的是完整的操作历史,reset 只是挪动指针,reflog 才是真正的后悔药索引。真正危险的是"还没提交的修改"。
  2. 所有分支/远程操作的前置动作是 git status:工作区不干净就切分支、合并,轻则修改被带跑,重则操作被拒。养成"先看状态,再动手"的条件反射。
  3. .gitignore 只作用于未跟踪文件:它对历史无能为力,处理已入库文件必须走 git rm --cached + 提交这一套。
  4. 规则写法决定作用范围:无斜杠 = 任意层级全匹配,前置斜杠 = 仅根目录,后置斜杠 = 仅目录,* 通配、! 取反。范围越宽越要谨慎。
  5. 冲突不是失败,是 Git 把决策权交还给人:冲突处理的固定套路永远是"改文件删标记 → add → commit",理解这条逻辑比背命令重要。
  6. 主分支只接收合并,不接受开发:master 上永远是可发布、经过测试的完整代码;半成品一律留在功能分支上,这是 GitFlow 所有角色的共同底线。
  7. 分支很便宜,别省:创建、切换、删除的成本几乎为零,因此"多开一个分支"永远比"在主分支上小心一点"更划算;功能分支用完即删,下次需要再建。
  8. 记状态比记命令更可靠:把"工作区 → 暂存区 → 版本库"三层模型想清楚,遇到没见过的命令组合也能自己推出来该往哪一步操作。