ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

School of SRE 的 Git 分支实战指南:从分支创建到 Merge 与 Rebase 的完整解析

School of SRE 的 Git 分支实战指南:从分支创建到 Merge 与 Rebase 的完整解析 教程【免费下载链接】school-of-sreAt LinkedIn, we are using this curriculum for onboarding our entry-level talents into the SRE role.项目地址https://gitcode.com/gh_mirrors/sc/school-of-sre点击查看免费下载Git 分支是并行开发多个特性、隔离不同工作流的核心机制。本文基于 LinkedIn School of SRE 课程课程大纲中 Working With Branches 一节的完整内容从底层提交树模型讲起逐步演示分支的创建、切换、并行提交并深入对比直接合并merge commit与「Rebase Fast-forward」两条合并路线。读完本文你将能够用git log --oneline --graph --all透视仓库的全貌理解HEAD与分支引用reference的本质并针对不同场景选择最合适的集成策略。为什么需要分支从单行历史到树状历史假设我们的本地仓库已经有两次提交此时提交历史是一条单行链提交df2fb7aadding file 1是7f3b00eadding file 2的父提交master指向最新的7f3b00e。$ git log --oneline --graph * 7f3b00e (HEAD - master) adding file 2 * df2fb7a adding file 1单行历史固然清晰但它意味着同一时刻仓库只有一个「前进方向」。当需要在同一个仓库里并行开发两个不同的特性时直观但笨拙的办法是复制一份代码到新文件夹、再单独git init一份仓库——代码重复、历史割裂、同步困难。更好的答案是分支branch。正如Git 基础章节中所强调的Git 内部以树状结构存储提交commit一个提交可以有两个或多个子提交而非只能单线串联。基于这种树状结构我们可以从任意一个提交上分出两条或多条分支每条分支独立承载一套特性的开发分支之间还可以通过合并merge重新汇聚。借助分支仓库中可以同时存在多条历史线我们可以随时checkout到其中任意一条上继续工作。所谓 checkout本质上就是用被检出版本snapshot的内容替换当前工作目录repo的内容——这一点在Git 基础章节中通过git checkout df2fb7a回到旧版本、ls只看到file1.txt已经得到验证。分支的本质指针而非代码副本在动手之前先建立正确的心理模型Git 内部就是一棵提交树分支名人类可读的名字是指向树中某个提交的指针pointer。我们使用各种 git 命令与这棵树和引用打交道Git 据此修改仓库内容。这个说法不是比喻而是有据可查的实现事实。Git 基础章节中的「The Magic」一节直接展示了引用的落盘形式$ cat .git/refs/heads/master 7f3b00eaa957815884198e2fdfec29361108d6a9master这个分支引用指向的提交 ID 就存在.git/refs/heads/master这个文件里。每当 Git 需要知道master指向哪里或需要更新master的指向都只需读写这个文件。同理HEAD也只是一个引用$ cat .git/HEAD ref: refs/heads/masterHEAD里存的是ref: refs/heads/master也就是说HEAD会跟随master指向的位置。无论你 checkout 到哪个提交或分支HEAD就指向哪里。理解了「分支指针」这一点下面所有命令的行为就顺理成章了。创建分支git branch b1回到当前仓库master指向7f3b00e创建一个名为b1的分支$ git branch b1 $ git log --oneline --graph * 7f3b00e (HEAD - master, b1) adding file 2 * df2fb7a adding file 1git log告诉我们两件事b1同样指向最后一次提交7f3b00e因为新分支默认从当前提交创建但HEAD仍然指向master即当前仍停留在master分支上工作。注意git branch只是创建分支并不会切换过去。切换分支git checkout b1将工作区切换到b1$ git checkout b1 Switched to branch b1 $ git log --oneline --graph * 7f3b00e (HEAD - b1, master) adding file 2 * df2fb7a adding file 1对比上一节输出b1仍指向同一个提交7f3b00e但HEAD的箭头从master移到了b1。由于我们从提交7f3b00e分叉从这个提交开始将出现两条历史线——你 checkout 在哪条分支上那条分支的历史线就会继续向前推进。并行开发两条独立的历史线在 b1 分支上提交此时我们处于b1分支新提交会把分支引用b1向前推进而当前b1的提交7f3b00e成为新提交的父提交# 创建文件并提交 $ echo I am a file in b1 branch b1.txt $ git add b1.txt $ git commit -m adding b1 file [b1 872a38f] adding b1 file 1 file changed, 1 insertion() create mode 100644 b1.txt # 新的历史线 $ git log --oneline --graph * 872a38f (HEAD - b1) adding b1 file * 7f3b00e (master) adding file 2 * df2fb7a adding file 1注意master仍然指向它原来指向的旧提交7f3b00e——两条分支此刻已经分道扬镳。回到 master 分支继续提交切换到master并做一次新提交会从7f3b00e出发形成另一条历史线# 切换到 master 分支 $ git checkout master Switched to branch master # 在 master 分支上创建新提交 $ echo new file in master branch master.txt $ git add master.txt $ git commit -m adding master.txt file [master 60dc441] adding master.txt file 1 file changed, 1 insertion() create mode 100644 master.txt # master 的历史线 $ git log --oneline --graph * 60dc441 (HEAD - master) adding master.txt file * 7f3b00e adding file 2 * df2fb7a adding file 1注意在master上执行git log时看不到b1分支的提交872a38f因为当前历史线不包含它。用 --all 查看全貌要同时看到两条历史线需要加上--all参数$ git log --oneline --graph --all * 60dc441 (HEAD - master) adding master.txt file || * 872a38f (b1) adding b1 file ||/ * 7f3b00e adding file 2 * df2fb7a adding file 1上图的树形结构一目了然在提交7f3b00e处出现了一个清晰的分叉fork。这就是创建分支的结果——现在b1与master是两条相互独立的历史线可以互不干扰地进行特性开发。合并Merge把特性带回主分支假设b1上的特性开发完成需要合并到master所有最终版本代码存放的分支。标准流程是先checkout到master再从upstream例如 GitHub 远端仓库pull最新的代码然后把自己的b1代码合并进来。合并有两条路线先回顾当前历史$ git log --oneline --graph --all * 60dc441 (HEAD - master) adding master.txt file || * 872a38f (b1) adding b1 file ||/ * 7f3b00e adding file 2 * df2fb7a adding file 1路线一直接合并产生 merge commit在master上直接执行git merge b1Git 会采用recursive递归策略合并两条历史线的改动并产生一个新的合并提交merge commit$ git merge b1 Merge made by the recursive strategy. b1.txt | 1 1 file changed, 1 insertion() create mode 100644 b1.txt $ git log --oneline --graph --all * 8fc28f9 (HEAD - master) Merge branch b1 |\ || * 872a38f (b1) adding b1 file * | 60dc441 adding master.txt file ||/ * 7f3b00e adding file 2 * df2fb7a adding file 1可以清楚看到一个新的合并提交8fc28f9诞生它有两个父提交872a38f与60dc441。执行合并时 Git 会提示输入提交信息commit message。这种方式的缺点也很明显如果仓库里分支很多会产生大量 merge commit历史图上布满分叉与汇合观感远不如一条干净的单线历史。下面看替代方案。撤销合并git reset --hard先撤销刚才的合并回到合并前的状态。此处使用git reset --hard commit将master硬重置到合并前的提交60dc441$ git reset --hard 60dc441 HEAD is now at 60dc441 adding master.txt file $ git log --oneline --graph --all * 60dc441 (HEAD - master) adding master.txt file || * 872a38f (b1) adding b1 file ||/ * 7f3b00e adding file 2 * df2fb7a adding file 1注意--hard会丢弃工作区与暂存区中未提交的改动使用时需确认没有需要保留的本地修改。路线二Rebase Fast-forward保持单线历史与其合并两条「底子相近」共同祖先提交7f3b00e的分支不如先把b1变基到当前master之上。所谓 rebase就是取出b1从共同祖先提交7f3b00e到872a38f之间的所有提交把它们重新「播放」在master60dc441的顶端# 切换到 b1 $ git checkout b1 Switched to branch b1 # 将当前分支b1变基到 master 上 $ git rebase master First, rewinding head to replay your work on top of it... Applying: adding b1 file # 结果 $ git log --oneline --graph --all * 5372c8f (HEAD - b1) adding b1 file * 60dc441 (master) adding master.txt file * 7f3b00e adding file 2 * df2fb7a adding file 1观察结果b1原本只有一个提交872a38f其父提交是7f3b00erebase 到master之后提交变成了全新的5372c8f其父提交变成了60dc441。作为副产品整个历史被整理成了一条单线。此时再合并b1到master就不再需要创建 merge commit——只需把master指针移动到5372c8f即b1指向的位置# 切回 master因为我们要把代码合并进 master $ git checkout master Switched to branch master # 当前历史b1 已基于 master $ git log --oneline --graph --all * 5372c8f (b1) adding b1 file * 60dc441 (HEAD - master) adding master.txt file * 7f3b00e adding file 2 * df2fb7a adding file 1 # 执行合并注意输出中的 Fast-forward 字样 $ git merge b1 Updating 60dc441..5372c8f Fast-forward b1.txt | 1 1 file changed, 1 insertion() create mode 100644 b1.txt # 结果 $ git log --oneline --graph --all * 5372c8f (HEAD - master, b1) adding b1 file * 60dc441 adding master.txt file * 7f3b00e adding file 2 * df2fb7a adding file 1输出中的Updating 60dc441..5372c8f与Fast-forward表明由于b1已经领先于master且没有分叉Git 只需把master引用快进到5372c8f无需产生新的合并提交。最终b1和master指向同一个提交代码成功合并进master可以 push 到远端并且历史是一条干净的单线。两种合并路线如何选择两条路线的取舍在分支章节中可以总结为维度直接git mergegit rebase Fast-forward 合并产物产生 merge commit历史呈树状分叉单线历史干净直观历史可读性分支多时 merge commit 堆积、观感杂乱始终是一条直线便于回溯与排查适用场景需要保留「何时合并、合并了谁」的真实汇合信息如发布分支个人特性分支、希望主分支保持整洁的日常开发两条路线各有权衡实践中可根据团队约定选择Git 也允许在git merge时通过--no-ff强制创建合并提交、或用--ff-only只允许快进合并来固化团队策略。从分支到协作与仓库其他模块的衔接分支、合并只是 Git 协作流程的一环。在 School of SRE 的课程体系中后续内容与分支直接相关Git 与 GitHub / Hooks 章节本地开发完成并通过 merge/rebase 之后还需要push到 GitHub 中央仓库、pull远端最新改动。该章节还介绍了pre-push、pre-commit等 hooks——例如可以配置pre-commit钩子在每次提交前执行脚本如代码检查这与「提交是否干净」直接相关。CI/CD 章节持续集成要求所有成员频繁地把改动推到各自的 feature 分支再由 CI 服务器在代码 push 后自动触发构建与测试。这正依赖分支机制提供的「独立开发、随时集成」能力——每个开发者一条分支改动随时可 push、可快速合并回主线。Git 结论章节掌握分支与合并之后还可以继续探索 cherry-pick、squash、amend、stash、reset 等进阶命令它们大多与「如何整理提交树、移动指针」这一核心模型同源。小结把分支当作「指针操作」贯穿本文的核心结论也是分支章节反复强调的Git 内部就是一棵提交树。分支名人类可读的名字是指向树中某个提交的指针。我们使用各种 git 命令来操作这棵树和引用Git 据此修改仓库内容。git branch b1从当前提交创建一个新指针不切换HEADgit checkout b1让HEAD指向b1并用b1指向提交的快照替换工作目录在两条分支上分别提交就形成从共同祖先分叉的两条独立历史线git merge直接汇合两条历史线产生 merge commitgit rebase master把当前分支的提交搬到master顶端然后git merge即可 Fast-forward得到干净的单线历史。从源码结构看这一切的「魔法」都沉淀在.git目录——refs/heads/下的文件记录每个分支指向的提交 IDHEAD文件记录当前检出位置详见 git-basics.md。理解了指针与树这两个概念再面对任何 Git 操作都会更有底气。赞分享教程【免费下载链接】school-of-sreAt LinkedIn, we are using this curriculum for onboarding our entry-level talents into the SRE role.项目地址https://gitcode.com/gh_mirrors/sc/school-of-sre点击查看免费下载相关推荐Whisper.cpp重新定义边缘计算的语音识别革命Whisper.cpp重新定义边缘计算的语音识别革命 在人工智能技术飞速发展的今天语音识别已经从云端服务逐渐向边缘设备迁移。如果你正在寻找一个既高效又隐私安人工智能语音音频本地部署推理引擎Git 分支实战从 Fork 到 Merge 的完整演练 —— curriculum 开源课程中的 Git Branches 项目指南Git 分支实战从 Fork 到 Merge 的完整演练 —— curriculum 开源课程中的 Git Branches 项目指南 本篇文章以开源课程仓库文档教程教育Git for Windows 开发指南从构建测试到 merging-rebase 分支丛维护实战Git for Windows 开发指南从构建测试到 merging rebase 分支丛维护实战 Git for Windows 是上游 Git 的一个 f版本控制开发工具CLI上一篇如何永久保存微信聊天记录WeChatExporter开源工具使用指南下一篇Microduck RL的sim2real完整配方为什么仿真会跑不等于真机会跑创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表