Git Is Not Fine

Git Is Not Fine

This is a piece about git. But I wrote it because of jj. The thing about jj is that I’m in love with it. I love it, and I’m convinced that you’ll love it too. I think that if jj doesn’t have any dealbreakers for you, you should give it a serious shot. But you probably won’t if you think git is fine. And that’s unfortunate, because git is not fine. 这是一篇关于 Git 的文章。但我写它是为了介绍 jj。关于 jj,我爱死它了。我热爱它,并且我相信你也会爱上它。我认为如果 jj 没有什么让你无法接受的硬伤,你应该认真尝试一下。但如果你觉得 Git 已经“挺好的”了,你可能就不会去尝试。这很遗憾,因为 Git 并不好。

See, Git does two jobs: it’s a distributed store for source, and it’s a distributed workflow tool. It knocked the first job out of the park so far that most of us fail to see that its solutions for the second job were mostly an afterthought. And if you actually work in a meaningfully distributed way (and whether you know it or not, you do — across time, with yourself or others) then whether you know it or not you are feeling the pain. Because, like East River Source Control says, async development is table stakes. 你看,Git 承担着两项工作:它是一个分布式源代码存储库,也是一个分布式工作流工具。它在第一项工作上做得极其出色,以至于我们大多数人都没意识到,它在第二项工作上的解决方案大多只是事后的补救。如果你确实以一种有意义的分布式方式工作(无论你是否意识到,你其实都在这样做——跨越时间,与自己或他人协作),那么无论你是否察觉,你都在承受这种痛苦。因为正如 East River Source Control 所言,异步开发是基本门槛。

Some Throatclearing About Git

关于 Git 的一些铺垫

If you’re not familiar with git (and you are), git is a distributed version control system, the first DVCS to hit critical mass and practically the only VCS anyone uses anymore. Almost every engineer who knows what a rebase is learned it using git commands, in terms of git constructs. It’s still a little miracle of a tool, too, economical and fast. 如果你不熟悉 Git(其实你很熟悉),Git 是一个分布式版本控制系统(DVCS),它是第一个达到临界规模的 DVCS,也是目前几乎所有人都在使用的唯一 VCS。几乎每个知道什么是 rebase 的工程师都是通过 Git 命令、在 Git 的概念框架下学会的。它仍然是一个小小的工具奇迹,经济且快速。

As a result, most all of us have seen or written little diagrams that look like this (which represents a local feature branch in a steady state): 因此,我们大多数人都见过或画过类似这样的图表(它代表了一个处于稳定状态的本地功能分支):

Diagrams like this are the heart of thinking in git: commits and branches. The commits are the source code and its history, and they are immutable. The branches are mutable pointers with a log attached. Behind these perfect diagrams hide devils, imperfections in git’s model of how we work with code. Let us uncover them. 像这样的图表是 Git 思维的核心:提交(commits)和分支(branches)。提交是源代码及其历史记录,它们是不可变的。分支是带有日志的可变指针。在这些完美的图表背后隐藏着魔鬼,即 Git 在我们处理代码的工作模型上的缺陷。让我们揭开它们。

There is No C

这里没有 C

Say you’re collaborating with someone in a faraway time zone. You don’t want to merge anything without getting their review first. How do maintain throughput in the presence of that time zone latency? The same way CPUs do it: by pipelining your work. Instead of writing a single PR, submitting it, and waiting for it to finish before starting the next one, you write the first PR, submit it, write the second PR on top of it, submit that one, and so on and so forth, submitting many sequential PRs for review simultaneously. 假设你正在与一个处于遥远时区的人协作。在没有获得对方审查之前,你不想合并任何东西。在存在时区延迟的情况下,如何保持吞吐量?就像 CPU 所做的那样:通过流水线化你的工作。与其写一个 PR、提交它、等待它完成再开始下一个,不如写第一个 PR 并提交,然后在它之上写第二个 PR 并提交,以此类推,同时提交多个连续的 PR 进行审查。

The term of art for this is “stacked PRs”. And unfortunately, git makes stacked PRs very hard to work with. To see why, let’s look at how a fastforward plus rebase flow is represented in git. 这种做法的专业术语叫“堆叠 PR”(stacked PRs)。不幸的是,Git 让堆叠 PR 的工作变得非常困难。为了弄清原因,让我们看看 Git 是如何表示“快进(fast-forward)加变基(rebase)”流程的。

The rebase takes the diff of C2 to C1 and applies it to the new commit we received from origin, C3, creating C2’. Those relationships are pretty clear in the diagram. That’s why people do the diagrams that way! Pro Git includes diagrams with exactly that shape. But these commit names are unlike anything you’d find in a real repo. Rebase 操作获取 C2 到 C1 的差异,并将其应用到我们从 origin 接收到的新提交 C3 上,从而创建 C2’。这些关系在图表中非常清晰。这就是为什么人们喜欢那样画图!《Pro Git》一书中就包含了这种形状的图表。但这些提交名称与你在真实仓库中找到的任何东西都不一样。

Take a moment to read these diagrams and the previous ones with fresh eyes, taking in what they point to in the underlying system. You might see then that we’ve lost some important information in the new diagrams. The two “Fix key entry race” commits had an ordered relationship indicated with an apostrophe. But that’s not there in the new diagrams. Git has no knowledge of that relationship, and can’t tell you about it. 花点时间用全新的视角审视这些图表以及之前的图表,看看它们在底层系统中指向了什么。你可能会发现,我们在新图表中丢失了一些重要信息。那两个“Fix key entry race”提交原本通过撇号(‘)表示了一种顺序关系。但在新图表中,这种关系消失了。Git 对这种关系一无所知,也无法告诉你。

So when we read classic git diagrams, or even these more detailed git diagrams, the diagrams themselves and sometimes even our own eyeballs are misleading us about the capabilities of our tool. There is no “C2” that you can look for and see various permutations of. There’s not even a “C” linking these commits together. These notions do not exist. 因此,当我们阅读经典的 Git 图表,甚至是这些更详细的 Git 图表时,图表本身,有时甚至是我们自己的眼睛,都在误导我们对工具能力的认知。根本不存在一个你可以查找并查看各种排列组合的“C2”。甚至没有一个“C”将这些提交连接在一起。这些概念根本不存在。

As a result, git commits cannot tell you and have no idea about: 因此,Git 提交无法告诉你,也根本不知道:

  • Successor commits (后继提交)
  • Revision history (if you amend a commit, you can’t get to the old one from the new one) (修订历史——如果你修改了一个提交,你无法从新提交找到旧提交)
  • Whether they are garbage or not (它们是否是垃圾数据)

Branches can’t do it either. They do have a notion of history, but: 分支也做不到。它们确实有历史的概念,但是:

  • Branches aren’t 1:1 with code changes. They are in some cases, but this is a convention you can’t rely on. (分支与代码变更并非一一对应。在某些情况下是,但这是一种不可靠的约定。)
  • Branches do not have relationships with one another. (分支之间没有关系。)

No Mutability

没有可变性

All of these issues flow downstream from git’s hands-off modeling of mutability. 所有这些问题都源于 Git 对可变性采取的“放任自流”的建模方式。