当前位置:

首页 > 软件教程 > Git 提交是版本差异、快照还是修改历史?

Git 提交是版本差异、快照还是修改历史?

理解Git提交实现方式对我来说很简单,但理解他人对提交的看法却有难度。因此,我在Mastodon上向他人提出了一些问题。大家是怎么看待Git提交的?我进行了一个非常不科学的调查,询问大家是怎么看待Git提交的:是快照、差异,还是所有之前提交的列表?(当然,把它看作这三者都是合理的,但我很好奇人们的主要结果是:51%差异42%快照4%所有之前的提交的历史记录3%“其他”我很惊讶差异和快照两个选项的比例如此接近。人们还提出了一些有趣但相互矛盾的观点,比如“在我看来,提交是一个差异,但我认为它实际上是以快照的形

本文目录

    Git 提交是差异、快照还是历史记录?

    理解Git提交实现方式对我来说很简单,但理解他人对提交的看法却有难度。因此,我在Mastodon上向他人提出了一些问题。

    大家是怎么看待 Git 提交的?

    我进行了一个 非常不科学的调查,询问大家是怎么看待 Git 提交的:是快照、差异,还是所有之前提交的列表?(当然,把它看作这三者都是合理的,但我很好奇人们的 主要

    结果是:

    • 51% 差异
    • 42% 快照
    • 4% 所有之前的提交的历史记录
    • 3% “其他”

    我很惊讶差异和快照两个选项的比例如此接近。人们还提出了一些有趣但相互矛盾的观点,比如
    “在我看来,提交是一个差异,但我认为它实际上是以快照的形式实现的” 和
    “在我看来,提交是一个快照,但我认为它实际上是以差异的形式实现的”。关于提交的实际实现方式,我们稍后再详谈。

    在我们进一步讨论之前:我们的说 “一个差异” 或 “一个快照” 都是什么意思?

    什么是差异?

    我说的“差异”可能相当明显:差异就是你在运行 git show COMMIT_ID 时得到的东西。例如,这是一个 rbspy 项目中的拼写错误修复:

    diff --git a/src/ui/summary.rs b/src/ui/summary.rs
    index 5c4ff9c..3ce9b3b 100644
    --- a/src/ui/summary.rs
    +++ b/src/ui/summary.rs
    @@ -160,7 +160,7 @@ mod tests {
    ";
    let mut buf: Vec = Vec::new();
    -stats.write(&mut buf).expect("Callgrind write failed");
    +stats.write(&mut buf).expect("summary write failed");
    let actual = String::from_utf8(buf).expect("summary output not utf8");
    assert_eq!(actual, expected, "Unexpected summary output");
    }

    你可以在 GitHub 上看到它: https://github.com/rbspy/rbspy/commit/24ad81d2439f9e63dd91cc1126ca1bb5d3a4da5b

    什么是快照?

    我说的 “快照” 是指 “当你运行 git checkout COMMIT_ID 时得到的所有文件”。

    Git 通常将提交的文件列表称为 “树”(如“目录树”),你可以在 GitHub 上看到上述提交的所有文件:

    https://github.com/rbspy/rbspy/tree/24ad81d2439f9e63dd91cc1126ca1bb5d3a4da5b(它是 /tree/ 而不是 /commit/)

    “Git 是如何实现的”真的是正确的解释方式吗?

    我最常听到的关于学习 Git 的建议大概是 “只要学会 Git 在内部是如何表示事物的,一切都会变得清晰明了”。我显然非常喜欢这种观点(如果你花了一些时间阅读这个博客,你就会知道我 喜欢

    但是作为一个学习 Git 的方法,它并没有我希望的那么成功!通常我会兴奋地开始解释 “好的,所以 Git
    提交是一个快照,它有一个指向它的父提交的指针,然后一个分支是一个指向提交的指针,然后……”,但是我试图帮助的人会告诉我,他们并没有真正发现这个解释有多有用,他们仍然不明白。所以我一直在考虑其他方案。

    但是让我们还是先谈谈内部实现吧。

    Git 是如何在内部表示提交的 —— 快照

    在内部,Git 将提交表示为快照(它存储每个文件当前版本的 “树”)。我在 在一个 Git 仓库中,你的文件在哪里? 中写过这个,但下面是一个非常快速的内部格式概述。

    这是一个提交的表示方式:

    $ git cat-file -p 24ad81d2439f9e63dd91cc1126ca1bb5d3a4da5b
    tree e197a79bef523842c91ee06fa19a51446975ec35
    parent 26707359cdf0c2db66eb1216bf7ff00eac782f65
    author Adam Jensen1672104452 -0500
    committer Adam Jensen1672104890 -0500
    Fix typo in expectation message

    以及,当我们查看这个树对象时,我们会看到这个提交中仓库根目录下每个文件/子目录的列表:

    $ git cat-file -p e197a79bef523842c91ee06fa19a51446975ec35
    040000 tree 2fcc102acd27df8f24ddc3867b6756ac554b33ef.cargo
    040000 tree 7714769e97c483edb052ea14e7500735c04713eb.github
    100644 blob ebb410eb8266a8d6fbde8a9ffaf5db54a5fc979a.gitignore
    100644 blob fa1edfb73ce93054fe32d4eb35a5c4bee68c5bf5ARCHITECTURE.md
    100644 blob 9c1883ee31f4fa8b6546a7226754cfc84ada5726CODE_OF_CONDUCT.md
    100644 blob 9fac1017cb65883554f821914fac3fb713008a34CONTRIBUTORS.md
    100644 blob b009175dbcbc186fb8066344c0e899c3104f43e5Cargo.lock
    100644 blob 94b87cd2940697288e4f18530c5933f3110b405bCargo.toml

    这意味着检出一个 Git 提交总是很快的:对 Git 来说,检出昨天的提交和检出 100 万个提交之前的提交一样容易。Git 永远不需要重新应用 10000 个差异来确定当前状态,因为提交根本就不是以差异的形式存储的。

    快照使用 packfile 进行压缩

    我刚刚提到了 Git 提交是一个快照,但是,当有人说 “在我看来,提交是一个快照,但我认为它在实现上是一个差异”
    时,这其实也是对的!Git
    提交并不是以你可能习惯的差异的形式表示的(它们不是以与上一个提交的差异的形式存储在磁盘上的),但基本的直觉是,如果你要对一个 10,000
    行的文件编辑 500 次,那么存储 500 份文件的效率会很低。

    Git 有一个将文件以差异的形式存储的方法。这被称为 “packfile”,Git 会定期进行垃圾回收,将你的数据压缩成 packfile 以节省磁盘空间。当你 git clone 一个仓库时,Git 也会压缩数据。

    这里,我没有足够的篇幅来完整地解释 packfile 是如何工作的(Aditya Mukerjee 的 《解压 Git packfile》是我最喜欢的解释它们是如何工作的文章)。不过,我可以在这里简单总结一下我对 deltas 工作原理的理解,以及它们与 diff 的区别:

    • 对象存储为 “原始文件” 和一个 “变化量delta” 的引用
    • 变化量是一系列例如 “读取第 0 到 100 字节,然后插入字节 ‘hello there’,然后读取第 120 到 200 字节” 的指令。它从原始文件中拼凑出新的文本。所以没有 “删除” 的概念,只有复制和添加。
    • 我认为变化量的层次较少:我不知道如何检查 Git 究竟要经过多少层变化量才能得到一个给定的对象,但我的印象是通常不会很多。可能少于 10 层?不过,我很想知道如何才能真正查出来。
    • 原始文件不一定来自上一个提交,它可以是任何东西。也许它甚至可以来自一个更晚的提交?我不确定。
    • 没有一个 “正确的” 算法来计算变化量,Git 只是有一些近似的启发式算法

    当你查看差异时,实际上发生了一些奇怪的事情

    当我们运行 git show SOME_COMMIT 来查看某个提交的差异时,实际上发生的事情有点反直觉。我的理解是:

  • Git 会在 packfile 中查找并应用变化量来重建该提交和其父提交的树。
  • Git 会对两个目录树(当前提交的目录树和父提交的目录树)进行差异比较。通常这很快,因为几乎所有的文件都是完全一样的,所以 git 只需比较相同文件的哈希值就可以了,几乎所有时候都不用做什么。
  • 最后 Git 会展示差异
  • 所以,Git 会将变化量转换为快照,然后计算差异。它感觉有点奇怪,因为它从一个类似差异的东西开始,最终得到另一个类似差异的东西,但是变化量和差异实际上是完全不同的,所以这是说得通的。

    也就是说,我认为 Git 将提交存储为快照,而 packfile 只是一个实现细节,目的是节省磁盘空间并加快克隆速度。我其实从来没必要知道 packfile 是如何工作的,但它确实能帮助我理解 Git 是如何在不占用太多磁盘空间的情况下将提交快照化的。

    一个 “错误的” Git 理解:提交是差异

    我认为一个相当常见的,对 Git 的 “错误” 的理解是:

    • 提交是以基于上一个提交的差异的形式存储的(加上指向父提交的指针和作者和消息)。
    • 要获取提交的当前状态,Git 需要从头开始重新应用所有之前的提交。

    这个理解当然是错误的(在现实中,提交是以快照的形式存储的,差异是从这些快照计算出来的),但是对我来说它似乎非常有用而且有意义!在考虑合并提交时会有一点奇怪,但是或许我们可以说这只是基于合并提交的第一个父提交的差异。

    我认为这个错误的理解有的时候非常有用,而且对于日常 Git 使用来说它似乎并没有什么问题。我真的很喜欢它将我们最常使用的东西(差异)作为最基本的元素——它对我来说非常直观。

    我也一直在思考一些其他有用但 “错误” 的 Git 理解,比如:

    • 提交信息可以被编辑(实际上不能,你只是复制了一个相同的提交然后给了它一个新的信息,旧的提交仍然存在)
    • 提交可以被移动到一个不同的基础上(类似地,它们是被复制了)

    我认为有一系列非常有意义的、 “错误” 的对 Git 的理解,它们在很大程度上都受到 Git 用户界面的支持,并且在大多数情况下都不会产生什么问题。但是当你想要撤销一个更改或者出现问题时,它可能会变得混乱。

    将提交视为差异的一些优势

    就算我知道在 Git 中提交是快照,我可能大部分时间也都将它们视为差异,因为:

    • 大多时候我都在关注我正在做的 更改 —— 如果我只是改变了一行代码,显然我主要是在考虑那一行代码而不是整个代码库的当前状态
    • 点击 GitHub 上的 Git 提交或者使用 git show 时,你会看到差异,所以这只是我习惯看到的东西
    • 我经常使用变基,它就是关于重新应用差异的

    将提交视为快照的一些优势

    但是我有时也会将提交视为快照,因为:

    • Git 经常对文件的移动感到困惑:有时我移动了一个文件并编辑了它,Git 无法识别它是否被移动过,而是显示为
      “删除了 old.py,添加了 new.py”。这是因为 Git 只存储快照,所以当它显示 “移动 old.py -> new.py”
      时,只是猜测,因为 old.py 和 new.py 的内容相似。
    • 这种方式更容易理解 git checkout COMMIT_ID 在做什么(重新应用 10000 个提交的想法让我感到很有压力)
    • 合并提交在我看来更像是快照,因为合并的提交实际上可以是任何东西(它只是一个新的快照!)。它帮助我理解为什么在解决合并冲突时可以进行任意更改,以及为什么在解决冲突时要小心。

    其他一些关于提交的理解

    Mastodon 的一些回复中还提到了:

    • 有关提交的 “额外的” 带外信息,比如电子邮件、GitHub 拉取请求或者你和同事的对话
    • 将“差异”视为一个“之前的状态 + 之后的状态”
    • 以及,当然,很多人根据情况的不同以不同的方式看待提交

    人们在谈论提交时使用的其他一些词可能不那么含糊:

    • “修订”(似乎更像是快照)
    • “补丁”(看起来更像是差异)

    就到这里吧!

    我很难了解人们对 Git 有哪些不同的理解。尤其棘手的是,尽管 “错误” 的理解往往非常有用,但人们却非常热衷于警惕 “错误”
    的心智模式,所以人们不愿意分享他们 “错误” 的想法,生怕有什么 Git 解释者会站出来向他们解释为什么他们是错的。(这些 Git
    解释者通常是出于善意的,但是无论如何它都会产生一种负面影响)

    但是我学到了很多!我仍然不完全清楚该如何谈论提交,但是我们最终会弄清楚的。

    感谢 Marco Rogers、Marie Flanagan 以及 Mastodon 上的所有人和我讨论 Git 提交。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    软件教程 git 提交
    相关文章 更多
    git解决代码冲突的详细步骤与操作方法
    git解决代码冲突的详细步骤与操作方法

    遇到Git合并冲突别慌张,本文详解冲突产生的原因及标准处理流程,涵盖查看冲突文件、理解标记含义、手动修改代码及完成合并提交的完整步骤,助你高效解决协作开发中的代码冲突问题。

    git创建分支bip的命令及完整操作步骤
    git创建分支bip的命令及完整操作步骤

    想知道如何用Git创建名为bip的分支?本文提供清晰的命令行操作步骤,包括本地创建、切换分支以及推送到远程仓库的方法,适合初学者快速上手。

    GitLab新手创建项目并推送第一次提交的操作指南
    GitLab新手创建项目并推送第一次提交的操作指南

    本文指导GitLab新手完成创建项目并推送第一次提交的最小闭环。涵盖远程项目创建、本地仓库初始化、添加远程地址及执行git push。重点说明HTTPS与SSH认证差异、分支名(master/main)核对及提交验证标准,确保远程仓库真正建立。

    在小说搜搜找不到想看的小说怎么办?小说搜搜换源功能使用详解【必学】
    在小说搜搜找不到想看的小说怎么办?小说搜搜换源功能使用详解【必学】

    小说搜搜搜索不到结果?五种实用解决方法帮你搞定 在小说搜搜里搜不到想看的书?这事儿其实挺常见。问题多半出在书源上——要么是默认的书源还没来得及收录,要么是原先的源数据失效了,更新没跟上。别急,下面这五条解决路径,基本能覆盖绝大多数情况,操作起来也不复杂。 一、通过阅读页调出换源入口手动切换 这个方法

    怎么看B站UP主直播的真实人数?在线观看人数换算教程
    怎么看B站UP主直播的真实人数?在线观看人数换算教程

    “实时人气”不等于真实在线人数,需通过区分标识、验证列表与弹幕、第三方工具比对及互动加权扣除四步估算 当你点开B站的某个直播间,屏幕上那个“实时人气:12.8万”的数字可能让你心头一震,觉得场面火热。然而细看之下,弹幕寥寥无几,互动屈指可数——这场景是不是有点眼熟?问题来了:那个醒目的“人气值”,真

    chrome搜索免验证入口
    chrome搜索免验证入口

    Chrome官方免验证入口为https://www.google.cn/chrome/,提供全平台安装包、免登录即用、本地化安全机制及引擎级性能优化。 到底该去哪里找正版、免费且无需繁琐验证的Chrome浏览器入口?这个问题困扰了不少网友。今天,我们就来直通核心,为大家详细拆解Chrome引擎的官方

    Mac导入Android项目指南
    Mac导入Android项目指南

    网上关于在mac上导入现有项目的android studio操作教程多种多样,以下是我在实际操作中成功导入项目所采用的具体步骤及关键注意事项,供各位开发者参考。 网上的教程确实五花八门,但核心流程其实万变不离其宗。根据多年经验,想要在Mac上把已有的Android项目顺利导入Android Stud

    过程式编程、面向对象编程、函数式编程、泛型编程、元编程
    过程式编程、面向对象编程、函数式编程、泛型编程、元编程

    命令式编程关注执行步骤,声明式描述目标,函数式强调无副作用。回调函数作为参数传递,可导致回调地狱,后由Promise解决。仿函数重载`operator()`,lambda更轻量。元编程生成代码,如模板、装饰器、宏,提升抽象能力。

    如何配置Python项目的.gitignore文件_排除环境相关冗余文件
    如何配置Python项目的.gitignore文件_排除环境相关冗余文件

    Python项目配置.gitignore应排除__pycache__、虚拟环境(venv/.venv等)、IDE生成文件(.idea/.vscode等)及缓存目录。误提交后需用`gitrm--cached`移除索引。建议复用GitHub官方模板并验证规则有效性。

    怎么在VSCode中查看Git冲突记录-图形化合并分支操作实操
    怎么在VSCode中查看Git冲突记录-图形化合并分支操作实操

    VSCode不保存历史冲突记录,仅在合并时实时标记冲突文件。冲突文件位于源代码管理面板的MERGECHANGES分组,带红色感叹号和冲突数量。点击文件以三栏合并编辑器显示当前分支、传入分支和结果区,供用户编辑解决冲突。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。