Git 版本控制可视化
编辑、暂存、提交、推送作用于不同区域。编辑修改工作区;git add 把选定内容写入索引和对象数据库;git commit 记录暂存树及其父提交;git push 传递提交与引用,不读取编辑器里尚未提交的内容。
本组课程从四个区域开始,进入对象数据库,再把可移动的分支名放到提交图上。合并先判断能否只移动引用,再处理分叉快照。变基把分支补丁复制到新父链上。远程协作增加第二个对象数据库;版本发布给旧提交命名;撤销操作区分复制文件、移动引用和追加反向提交。
区域边界
Git 的四个区域追踪同一文件在工作区、索引、本地仓库和远程仓库中的状态。关键场景是在 git add 后继续编辑:工作区为 two,索引仍为 one,下一次提交记录 one。暂存把选定内容与后续编辑隔开。
本地提交也不等于推送。本地可以领先远程,工作区内容必须进入可达提交,才能随正常推送传到远端。
对象模型
提交即快照打开本地仓库。文件字节形成 blob,索引生成 tree,commit 记录 tree 和 parent。只修改 app.py,新 tree 会复用旧 README.md 的 blob。允许空提交时,整棵 tree 可以复用,但父提交字段变化仍会产生新 commit ID。
Git 用内容寻址的不可变对象保存数据,用可移动的名字指向对象。HEAD 移动时,对象不会跟着移动;改变的是引用。
引用模型
分支与 HEAD让引用变化可见。main 与 feature 最初指向同一个提交。在 feature 提交,只移动 feature;切回 main,改变 HEAD,不移动两个分支引用;随后提交,只推进 main。分叉共享原有对象,不复制两套历史。
游离 HEAD 场景跳过分支名。新提交直接推进 HEAD,所有分支保持原位。未建立保留分支就切走后,该提交不再被分支引用,但对象仍可能保存在仓库中,并可通过 reflog 找回。
快进合并
快进合并从同一父链上的 main 与 feature 开始,main 落后。Git 从来源分支顶端沿父链查找当前顶端。证明包含关系后,只需把 main 移到 feature 已指向的提交。来源引用不动,也不创建合并提交。
两个顶端相同,或来源已包含在当前历史中,都是无需更新。双方在共同基点后各有新提交时,--ff-only 拒绝合并,不移动引用。
合并分叉快照
三路合并接着处理分叉。标记当前顶端可达的提交,检查来源祖先,选择本课唯一的最佳共同祖先。它提供 base,两个分支顶端提供 ours 与 theirs。
默认场景中,main 修改 app.py,feature 修改 README.md。逐路径比较后,各取有改动的一方。所有路径解决后才写结果 tree,再创建以两个旧顶端为有序父提交的 commit,最后推进 main。来源引用不动。
冲突场景让双方对同一路径作不同改动。执行停在 tree、commit 和引用写入之前,区分合并规划与结果记录。
解决冲突
合并冲突展示停止后的状态。HEAD 和当前分支不动,MERGE_HEAD 记录来源顶端。索引的 stage 0 被 stage 1/base、stage 2/ours、stage 3/theirs 替代。工作文件中的冲突标记只是可编辑的字节。
默认流程先编辑文件,检查索引仍未合并,再运行 git add app.txt,把三个阶段条目归并成一个 stage-0 blob。之后 git merge --continue 才能写 tree、创建双父提交,并只推进 main。
只编辑而不 add,冲突状态不会解除。反过来,带着冲突标记 add 也会被接受,因为 Git 暂存的是文件字节,无法判断你的解决意图。
用变基重写分支
变基找出当前分支独有的提交,按从旧到新排列,把补丁依次应用到目标顶端。补丁与消息保留,父提交字段改变,因而产生新的提交对象和 ID。成功后分支引用移到新链,旧链对象仍存在。
冲突场景展示恢复所需的引用规则。临时游离 HEAD 随每次成功重放推进,命名分支仍指向原顶端。REBASE_HEAD 标记失败补丁对应的原提交。git rebase --abort 恢复原来的附着分支状态,不必撤销一连串分支引用移动。
远程协作
远程协作区分本地 main、本地缓存 origin/main、远程仓库自己的 main。队友先推送后,本地两个引用都可能过期。Fetch 复制缺少的对象,只推进 origin/main;快进 pull 再推进本地 main 并更新检出内容。产生新的本地提交后,push 传送对象、推进远程分支,并刷新跟踪引用。
分叉时,pull --ff-only 保留 fetch 的结果,但拒绝推进本地 main。Push 检查服务器的实际顶端,不信任过期的本地跟踪引用;非快进请求会被拒绝,不改变远程持久状态。
标记发布版本
标签与发布区分三种对象。轻量标签是直接指向对象的引用。附注标签指向 tag 对象,后者再指向提交。托管平台的 release 则是 Git 对象数据库之外的平台记录,本课在发布时记录标签解引用得到的源提交。
默认流程发布 v1.0.0,推进 main,再检出旧版本。标签与发布源仍指向发布时的提交,分支继续前进;HEAD 游离在标签最终指向的提交上。强制移动标签的场景说明:标签引用可以被显式改写,当前目标可能与发布记录中的源提交不同。
撤销改动
撤销改动同时展示工作区、索引、HEAD 的 tree、选定提交、main 和 ORIG_HEAD。Restore 把快照复制到文件层;reset 移动 main,再由模式决定索引与工作区是否跟随;revert 计算反向改动并追加提交,原错误提交仍留在祖先链中。
默认流程都让文件回到 mode=safe,但相同文本不代表相同历史。Reset 移动引用,不增加对象;revert 新增一个提交,并保留旧顶端的可达性。Hard reset 场景还说明:ORIG_HEAD 能记录旧提交,不能保存从未写入 Git 的工作区字节。
建议学习顺序
- Git 的四个区域:先能说清每条命令读写哪些区域。
- 提交即快照:查看真实 Git 格式的对象 ID,区分快照 tree 与 commit。
- 分支与 HEAD:对象保持不变,观察名字与 HEAD 的移动。
- 快进合并:先证明祖先关系,再移动当前分支。
- 三路合并:寻找共同基点,逐路径解决,生成 tree 和双父提交。
- 合并冲突:在索引与工作区检查解决结果,理解 add 的复制边界。
- 变基:复用基点与冲突规则,按从旧到新创建单父提交;全部成功前,命名分支保持原位。
- 远程协作:把对象、引用和祖先规则用于两个仓库,区分本地知识与远程事实。
- 标签与发布:结合不可变对象、可移动引用、游离 HEAD 和平台元数据,确认版本发布了哪个提交。
- 撤销改动:判断操作是复制快照、移动名字,还是追加修正。
全部 Git 版本控制课程
先运行每课默认流程,再用沙盒逐项检验:未暂存就提交;比较普通与空提交;游离 HEAD 后切走;比较线性与已包含历史;比较干净合并与冲突;暂存带标记的文件;变基成功重放一次后中止;制造本地与远程分叉;强制移动已发布标签;最后比较 hard reset 与 revert,观察文件相同而历史不同的结果。