详细翻译如下(沉浸式翻译)
<details>
The open-source Git project just released Git 2.54 with features and bug fixes from over 137 contributors, 66 of them new. We last caught up with you on the latest in Git back when 2.52 was released.
开源的 Git 项目刚刚发布了 Git 2.54,包含了来自 137 名贡献者的功能和漏洞修复,其中 66 位是新贡献者。我们上次和你聊聊最新的 Git 是在 2.52 发布的时候。
To celebrate this most recent release, here is GitHub’s look at some of the most interesting features and changes introduced since last time.
为了庆祝这次最新发布,以下是 GitHub 对自上次以来引入的一些最有趣的功能和变化的回顾。
💡 Since the last Git release we wrote about was Git 2.52, this blog post covers the highlights from both the 2.53 and 2.54 releases.
💡 由于我们上一次写到的 Git 发布是 Git 2.52,这篇博客文章涵盖了 2.53 和 2.54 版本的亮点。
Rewrite history with git history
用 git 历史重写历史
The Git project has a long history of providing tools to rewrite your repository’s history. git rebase –i is the most well-known, and it’s remarkably flexible: you can reorder, squash, edit, and drop commits. But that flexibility comes with complexity: an interactive rebase operates on a range of commits, updates your working tree and index as it goes, and can leave you in a conflicted state that you need to resolve before proceeding.
Git 项目有着悠久的历史,致力于提供重写仓库历史的工具。Git Rebase –I 是最知名的,它非常灵活:你可以重新排序、压缩、编辑和丢弃提交。但这种灵活性也带来了复杂性:交互式重基操作于一系列提交,实时更新工作树和索引 ,可能会让你处于冲突状态,需要解决后才能继续。
For simpler cases, all of that machinery can feel like overkill. If all you want to do is fix a typo in a commit message three commits back, or split one commit into two, an interactive rebase works, but requires you to set up a to-do list, mark the right commit for editing, and then drive the rebase to completion.
对于更简单的情况,所有这些机制可能会显得过于繁复。如果你只是想修正提交消息中的错别字,或者把一个提交拆成两个,交互式重基是可以的,但需要你设置待办事项列表,标记正确的提交以便编辑,然后推动重基完成。
Git 2.54 introduces a new experimental command that is designed for exactly these simpler cases: git history. The history command currently supports two operations: reword and split.
Git 2.54 引入了一个新的实验命令,专门针对这些更简单的情况:git history。 历史命令目前支持两种操作: 重写和拆分 。
git history reword <commit> opens your editor with the specified commit’s message and rewrites it in place, updating any branches that descend from that commit. Unlike git rebase, it doesn’t touch your working tree or index, and it can even operate in a bare repository.
git history reword <commit> 会打开编辑器,显示指定提交的消息,并重新编写,更新该提交的分支。与 git rebase 不同,它不涉及你的工作树或索引,甚至可以在裸仓库中运行。
git history split <commit> lets you interactively split a commit into two by selecting which hunks should be carved out into a new parent commit. The interface will look familiar if you’ve ever used add in interactive mode via git add –p:
git 历史分割 <commit> 允许你通过选择哪些块块被划分为新的父提交,交互式地将提交分成两部分。如果你曾经通过 git add 的交互模式使用 add,这个界面会很熟悉 –p:
$ git history split HEAD
diff --git a/bar b/bar
new file mode 100644
index 0000000..50810a5
--- /dev/null
+++ b/bar
@@ -0,0 +1 @@
+bar
(1/1) Stage addition [y,n,q,a,d,p,?]? y
After selecting hunks, Git creates a new commit with those changes as the parent of the original commit (which retains whatever hunks you didn’t select) and rewrites any descendent branches to point at the updated history.
选中 hunks 后,Git 会创建一个新的提交,这些更改作为原始提交的父(保留你未选中的 hunk),并重写后代分支指向更新后的历史。
There are a couple of intentional limitations worth noting. The history command does not support histories that contain merge commits, and it will refuse to perform any operation that would result in a merge conflict. By design, git history is meant for targeted, non-interactive rewrites, not the kind of open-ended history rewriting typically relegated to git rebase –i.
这里有几个刻意的限制值得注意。历史命令不支持包含合并提交的历史,并且会拒绝执行任何可能导致合并冲突的操作。从设计上讲,git 历史是为有针对性的、非交互式的重写而设计的,而不是那种通常只限于 git 重写 –i 的开放式历史重写。
The history command is built on top of git replay‘s core machinery, which was itself extracted into a library as part of this work. That foundation means that git history benefits from replay‘s ability to operate without touching the working tree, making it a natural fit for scripting and automation in addition to interactive use.
历史命令建立在 git replay 的核心机制之上,而这些核心机制本身也被提取到库中。这种基础使得 git 历史受益于 replay 无需操作工作树的操作能力,使其在脚本编写和自动化中自然而然地适合交互式使用。
This command is still marked as experimental, so its interface may evolve. Give it a try with git history reword and git history split, available in Git 2.54.
该命令仍标记为实验性,因此其接口可能会演变。可以试试用 Git 历史 Reword 和 Git 历史分割 ,Git 2.54 版本中都有。
[source, source, source, source, source]
[ 来源 , 来源 , 来源 , 来源 , 来源 ]
Config-based hooks 基于配置的钩子
If you’ve ever wanted to share a Git hook across multiple repositories, you’ve probably had to reach for a third-party hook manager, or manually symlink scripts into each repository’s $GIT_DIR/hooks directory. That’s because, historically, Git hooks could only be defined as executable scripts living in one place: the hooks subdirectory of your .git directory (or whatever core.hooksPath points to).
如果你曾经想在多个仓库共享一个 Git 钩子 ,你可能不得不使用第三方的钩子管理器,或者手动将脚本符号链接到每个仓库的 $GIT_DIR/hooks 目录中。这是因为历史上,Git 钩子只能定义为存在于一个地方的可执行脚本:你 .git 目录的钩子子目录(或 core.hooksPath 指向的地址)。
That meant that if you wanted to run a linter before every commit across all of your repositories, you had to copy the script into each repository, which can be tedious and error-prone. Alternatively, you could set core.hooksPath to point to a shared directory, but that causes all of your repositories to share the exact same set of hooks, with no way to mix and match.
这意味着如果你想在所有仓库的每次提交前运行一个 linter,就必须把脚本复制到每个仓库,这既繁琐又容易出错。或者,你可以设置 core.hooksPath 指向共享目录,但那样会导致所有仓库共享完全相同的钩子集,无法混搭。
Git 2.54 introduces a new way to define hooks: in your configuration files. Instead of placing a script at .git/hooks/pre-commit, you can now write:
Git 2.54 引入了一种定义钩子的新方式:在配置文件中。你不再把脚本放在 .git/hooks/pre-commit,而是可以写:
[hook "linter"]
event = pre-commit
command = ~/bin/linter --cpp20
The hook.<name>.command key specifies the command to run, and hook.<name>.event specifies which hook event should trigger it. Since this is just configuration, it can live in your per-user ~/.gitconfig, a system-wide /etc/gitconfig, or in a repository’s local config. That makes it straightforward to define a set of hooks centrally and have them apply everywhere.
hook.<name>.command 键指定执行命令,hook.<name>.event 指定触发哪个钩子事件。因为这只是配置,它可以存在于每个用户的 ~/.gitconfig、系统范围的 /etc/gitconfig,或者仓库的本地配置中。这样就很容易在中心范围内定义一组挂钩,并让它们适用于所有地方。
Even better, you can now run multiple hooks for the same event. If you want both a linter and a secrets scanner to run before every commit, you can configure them independently:
更棒的是,你现在可以为同一事件跑多个挂钩。如果你想在每次提交前同时运行 linter 和 secrets 扫描仪,可以分别配置它们:
[hook "linter"]
event = pre-commit
command = ~/bin/linter --cpp20
[hook "no-leaks"]
event = pre-commit
command = ~/bin/leak-detector
Git will run them in the order it encounters their configuration. The traditional hook script in $GIT_DIR/hooks still works, and runs last, so existing hooks are unaffected. You can see which hooks are configured (and where they come from) with git hook list:
Git 会按照遇到配置的顺序运行它们。$GIT_DIR/hooks 中的传统钩子脚本依然有效,并且运行在最后,所以现有的钩子不受影响。你可以用 git hook 列表看到哪些钩子被配置了(以及它们来自哪里):
$ git hook list pre-commit
global linter ~/bin/linter --cpp20
local no-leaks ~/bin/leak-detector
Individual hooks can be disabled without removing their configuration by setting hook.<name>.enabled = false, which is particularly handy when a hook is defined in a system-level config but you need to opt a specific repository out.
可以通过设置 hook.<name>.enabled = false 来禁用单个钩子而不移除配置,这在系统级配置中定义钩子但需要退出特定仓库时尤其方便。
Along the way, Git’s internal handling of hooks has been modernized. Many built-in hooks that were previously invoked through ad-hoc code paths (like pre-push, post-rewrite, and the various receive-pack hooks) have been migrated to use the new hook API, meaning they all benefit from the new configuration-based hook machinery.
在此过程中,Git 内部对钩子的处理得到了现代化。许多之前通过临时代码路径调用的内置钩子(如推送前 、 重写后和各种接收包钩子)已被迁移到新的钩子 API,这意味着它们都受益于基于配置的钩子机制。
[source, source, source]
[ 来源 , 来源 , 来源 ]
Geometric repacking during maintenance by default
默认维护期间的几何重新打包
Returning readers of this series may recall our coverage of the new geometric strategy within git maintenance, which was introduced in Git 2.52. That strategy works by inspecting the contents of your repository to determine if some number of packfiles can be combined to form a geometric progression by object count. If they can, Git performs a geometric repack, condensing the contents of your repository without needing to perform a full garbage collection.
本系列的老读者可能还记得我们对 git 维护中新几何策略的介绍,该策略是在 Git 2.52 中引入的。该策略通过检查仓库内容,确定是否可以组合一定数量的包文件 ,形成按对象数量形成几何级数 。如果可以,Git 会执行几何重打包,压缩仓库内容,而无需进行完整的垃圾回收。
In 2.52, the geometric strategy was available as an opt-in choice via the maintenance.strategy configuration. In 2.54, it becomes the default strategy for manual maintenance. That means when you run git maintenance run without specifying a strategy, Git will now use the geometric approach instead of the traditional gc task.
在 2.52 版本中, 几何策略可以通过 maintenance.strategy 配置作为选择选项提供。在 2.54 版本中,它成为手动维护的默认策略。这意味着当你运行 git 维护运行时没有指定策略,Git 现在会用几何方法代替传统的 GC 任务。
In practice, this means that your repositories will be maintained more efficiently out of the box. The geometric strategy avoids the expensive all-into-one repacks that gc performs, instead combining packs incrementally when possible and falling back to a full gc only when it would consolidate the entire repository into a single pack. Along the way, it keeps your commit-graph, reflogs, and other auxiliary data structures up to date.
实际上,这意味着你的仓库开箱即用时就能更高效地维护。几何策略避免了 gc 进行的昂贵的全合一重打包,而是尽可能地逐步合并包,只有在将整个仓库合并为一个包时才回退到完整 GC。在此过程中,它会让你的提交图 、reflog 和其他辅助数据结构保持最新。
If you were already using maintenance.strategy = geometric in your configuration, nothing changes. If you hadn’t set a strategy (or were relying on the old gc default), you’ll start seeing the benefits of geometric repacking automatically. The gc strategy is still available if you prefer it and can be selected with maintenance.strategy = gc.
如果你配置中已经用了 maintenance.strategy = 几何, 那就没什么变化。如果你没有设定策略(或者一直依赖旧的 GC 默认),你会开始看到几何重新包装自动带来的好处。如果你喜欢,GC 策略仍然可用,并且可以用 maintenance.strategy = gc 来选择。
[source] [ 来源 ]
The tip of the iceberg…
冰山一角......
Now that we’ve covered some of the larger changes in more detail, let’s take a closer look at a selection of some other new features and updates in this release.
既然我们已经更详细地介绍了一些较大的变动,接下来让我们仔细看看本次发布中的一些其他新功能和更新。
The git add –p command, Git’s tool for interactively staging individual hunks, received a handful of usability improvements in this release. When navigating between hunks with the J and K keys, Git now shows whether you’ve previously accepted or skipped each hunk, so you don’t have to remember your earlier decisions.
git add –p 命令,这是 Git 用于交互式地分拣单个块子的工具,在本版本中获得了一些易用性的改进。用 J 键和 K 键在块块之间 切换时,Git 现在会显示你之前是否 接受 了或跳过了每个块块,这样你 就不用 记得之前的决定了。
Separately, a new --no-auto-advance flag changes how git add –p handles the transition between files. Normally, once you’ve made a decision on every hunk in a file, the session automatically moves on to the next one. With --no-auto-advance, the session stays put after you’ve decided on the last hunk, letting you use < and > to move between files at your own pace. This can be useful when you want to review your decisions holistically before committing to them.
另外,新的 --no-auto-advance 标志改变了 git add –p 处理文件间转换的方式 。 通常,一旦你对文件中的每个 块做出决定,会话就会自动进入下一个。 使用 --no-auto-advance 时,在你 决定最后一个块后 ,会话会保持不动, 你可以用 < 和 > 按自己的节奏在文件间移动。当你想在做出 决定前 全面审视时,这非常有用。
[source, source]
[ 来源 , 来源 ]
git replay, the experimental command for replaying commits onto a new base without touching the working tree, continues to mature. This release brings several improvements: replay now performs atomic reference updates by default (instead of printing update-ref commands to stdout), has learned a new --revert mode that reverses the changes from a range of commits, can now drop commits that become empty during replay, and supports replaying all the way down to the root commit.
Git Replay,一种实验性命令,用于在不动工作树的情况下重放提交到新基底,持续成熟。本次发布带来了多项改进: replay 现在默认执行原子引用更新(不再向 stdout 打印 update-ref 命令 ),学习了一个新的 ---revert 模式,可以逆转一系列提交的更改,现在可以丢弃重放时变空的提交,并支持从根提交处重放。
[source, source, source, source]
[ 来源 , 来源 , 来源 , 来源 ]
Git’s HTTP transport now handles HTTP 429 “Too Many Requests” responses. Previously, a 429 from the server would be treated as a fatal error. Git can now retry the request, honoring the server’s Retry-After header when present, or fall back to a configurable delay via the new http.retryAfter setting. The new http.maxRetries and http.maxRetryTime configuration options provide control over how many times to retry and how long to wait, respectively.
Git 的 HTTP 传输现在处理 HTTP 429 的“请求过多” 响应。以前,服务器端的 429 会被视为致命错误。Git 现在可以重试请求,在存在时尊重服务器的 Retry-After 头部,或者 通过新的 http.retryAfter 设置回退 到可配置的延迟。 新的 http.maxRetries 和 http.maxRetryTime 配置选项可以控制重试次数和等待时间, 分别。
[source] [ 来源 ]
git log –L, which traces the history of a range of lines within a file, has historically used its own custom output path that bypassed much of Git’s standard diff machinery. As a result, it was incompatible with several useful options, including the -S and -G “pickaxe” options for searching by content changes.
git log –L 用于追踪文件中一系列行的历史,历史上使用自己的自定义输出路径,绕过了 Git 的许多标准差分机制。因此,它与多个有用的选项不兼容,包括用于内容变更搜索的 -S 和 -G “镐”选项。
This release reworks git log –L to route its output through the standard diff pipeline, making it compatible with patch formatting options and pickaxe searches for the first time.
本版本重做了 git log –L,使其输出通过标准 diff 管道,首次兼容音色格式选项和镐头搜索。
Say you want to trace the history of strbuf_addstr() in strbuf.c, but only see commits where len was added or removed within that function:
假设你想在 strbuf.c 中追踪 strbuf_addstr() 的历史,但只看到在该函数中添加或移除 len 的提交:
$ git log -L :strbuf_addstr:strbuf.c -S len --oneline -1
a70f8f19ad2 strbuf: introduce strbuf_addstrings() to repeatedly add a string
diff --git a/strbuf.c b/strbuf.c
--- a/strbuf.c
+++ b/strbuf.c
@@ -316,0 +316,9 @@
+void strbuf_addstrings(struct strbuf *sb, const char *s, size_t n)
+{
- size_t len = strlen(s);
+
- strbuf_grow(sb, st_mult(len, n));
- for (size_t i = 0; i < n; i++)
- strbuf_add(sb, s, len);
+}
Prior to this release, options like -S, (and -G, --word-diff, along with --color-moved) were silently ignored when used with -L. Now they work together naturally: -L scopes the output to the function you care about, and -S filters down to just the commits that touched the symbol you’re searching for within it.
在此之前,使用 -L 时,像 -S、--word-diff 以及 --color-moved 这样的选项被默许。 现在它们自然地协同工作:-L 将输出范围映射到你关心的函数,-S 则过滤到只涉及你在其中搜索符号的提交。
[source] [ 来源 ]
Incremental multi-pack indexes, which we first covered in our discussion of Git 2.47 and followed up on in Git 2.50, received further work in this release. The MIDX machinery now supports compaction, which merges smaller MIDX layers together (along with their associated reachability bitmaps) to keep the number of layers in the chain manageable. This is an important step toward making incremental MIDXs practical for long-lived repositories that accumulate many layers over time.
增量多包索引,我们最初在讨论 Git 2.47 时提及,并在 Git 2.50 中 进行了后续处理,在本版本中得到了进一步的工作。MIDX 机制现在支持 压缩 ,将较小的 MIDX 图层(及其相关的可达位图)合并在一起,以保持链中层数的可控性。这是 使增量 MIDX 适用于长期积累多层存储库的重要一步 。
[source] [ 来源 ]
git status learned a new status.compareBranches configuration option. By default, git status shows how your current branch compares to its configured upstream (e.g., “Your branch is ahead of ‘origin/main’ by 3 commits”). With status.compareBranches, you can ask it to also compare against your push remote, or both:
git status 学习了一个新的 status.compareBranches 配置选项。默认情况下,git 状态显示你当前分支与其配置上游的比较(例如,“你的分支比'origin/main'快 3 个提交”)。通过 status.compareBranches,你可以让它也与你的推送遥控器进行比较,或者两者都做:
[status]
compareBranches = @{upstream} @{push}
This is useful if your push destination differs from your upstream, as is common in triangular workflows where you fetch from one remote and push to another (like a fork).
如果你的推送目的地和上游不同,这很有用,比如三角工作流程中常见的做法,比如从一个远程取回并推送到另一个(比如分支)。
[source] [ 来源 ]
Say you have a series of commits, and want to add a trailer to each one of them. You could do this manually, or automate it with something like: git rebase -x ‘git commit --amend --no-edit --trailer=”Reviewed-by: A U Thor <a href="mailto:author@example.com”’, but that’s kind of a mouthful.
假设你有一系列提交,想为每个提交添加一个预告片。你可以手动操作,或者用类似 :git rebase -x 'git commit --amend --no-edit --trailer=“Reviewed-by: A U Thor <a href=”mailto:author@example.com“',但这有 点 难说。
In Git 2.54, git rebase learned a new --trailer option, which appends a trailer to every rebased commit via the interpret-trailers machinery. Instead of the monstrosity above, we can now write git rebase --trailer "Reviewed-by: A <a href="mailto:a@example.com" and achieve the same effect.
在 Git 2.54 中,git rebase 学习了一个 新的 --trailer 选项,通过 interpret-trailers 机制为每个重构提交 附加一个 trailer。 现在我们可以写 成 git rebase --trailer “Reviewed-by: A <a href=”mailto:a@example.com“, 实现同样的效果。
[source] [ 来源 ]
When signing commits, a signature remains valid even when it was signed with a GPG key that has since expired. Previously, Git displayed these signatures with a scary red color, which could be misleading and lead you to interpret the signature itself as invalid. Git now correctly treats a valid signature made with a since-expired key as a good signature.
签署提交时, 即使签名用的 GPG 密钥已过期,签名依然有效。 此前,Git 会用令人 害怕的红色显示这些签名 ,这可能会误 导你,让你误以为签名本身无效。Git 现在正确地将用已过期密钥创建的有效签名视为良好签名。
[source] [ 来源 ]
git blame learned a new --diff-algorithm option, allowing you to select which diff algorithm (e.g., histogram, patience, or minimal) is used when computing blame. This can sometimes produce meaningfully different (and more useful) blame output, depending on the nature of the changes in your repository’s history.
Git Blame 学习了一个新的 --diff-算法 选项,允许你在计算 blame 时选择使用哪 种微分算法 (例如 直方图 、 耐心 或最小值)。这有时会根据仓库历史变更的性质产生有意义且更有用的责任输出。
[source] [ 来源 ]
Under the hood, a significant amount of work went into restructuring Git’s object database (ODB) internals. The ODB source API has been refactored to use a pluggable backend design, with individual functions like read_object(), write_object(), and for_each_object() now dispatched through function pointers on a per-source basis. While none of this is user-visible today, it lays the groundwork for future features like alternative storage backends or more flexible object database configurations.
在底层,大量工作被用于重构 Git 的对象数据库(ODB)内部结构。ODB 源代码 API 已被重构为可插拔后端设计,像 read_object())、 write_object() 和 for_each_object() 这样的单个函数现在通过函数指针按每个源代码分发。虽然这些功能目前用户都看不到,但它 为 未来功能奠定了基础,比如替代存储后端或更灵活的对象数据库配置。
[source, source, source, source]
[ 来源 , 来源 , 来源 , 来源 ]
git backfill, the experimental command for downloading missing blobs in a partial clone, learned to accept revision and pathspec arguments. Previously, backfill would always download blobs reachable from HEAD across the entire tree. You can now scope it to a particular range of history (e.g., git backfill main100..main) or a subset of paths (e.g., git backfill -- '.c'), including pathspecs with wildcards.
git backfill,用于部分克隆中下载缺失 blob 的实验命令,学会了接受修订和 路径规范 参数。以前, 回填 总是会从 HEAD 在整个树上下载可 访问的 blobs。你现在可以将范围设置为特定的历史范围(例如, git backfill main100..main)或路径子集(例如 git backfill -- '.c'),包括 带有万用符的路径规格 。
This makes backfill much more practical for large partial clones where you only need historical blobs for a specific area of the repository.
这使得回填对于大型部分克隆更实用,因为你只需要为存储库的特定区域设置历史斑块。
[source] [ 来源 ]
Git’s alias configuration has historically been limited to ASCII alphanumeric characters and hyphens. That meant alias names like “hämta” (Swedish for “fetch”) or “状態” (Japanese for “status”) were off-limits. Git 2.54 lifts that restriction with a new subsection-based syntax:
Git 的别名配置历来仅限于 ASCII 字母数字字符和连字符。这意味着像“hämta”(瑞典语意为“取物”)或“状态”(日语意为“地位”)这样的别名是禁止使用的。Git 2.54 通过新的基于子部分的语法消除了这一限制:
[alias "hämta"]
command = fetch
The traditional [alias] co = checkout syntax continues to work for ASCII names. The new subsection form supports any characters (except newlines and NUL bytes), is matched case-sensitively as raw bytes, and uses a command key for the alias definition. Shell completion has been updated to handle these aliases as well.
传统的 [别名] co = 借出语法仍然适用于 ASCII 名称。新的子节形式支持任意字符(换行和 NUL 字节除外),以原始字节区分大小写匹配,并使用命令键来定义别名。壳体补全也已更新以处理这些别名。
[source, source]
[ 来源 , 来源 ]
The histogram diff algorithm received a fix for a subtle output quality issue. After any diff algorithm runs, Git performs a “compaction” phase that shifts and merges change groups to produce cleaner output. In some cases, this shifting could move a change group across the anchor lines that the histogram algorithm had chosen, producing a diff that was technically correct but visually redundant. Git now detects when this happens and re-diffs the affected region, resulting in tighter output that better matches what you would expect.
直方图微分算法修复了一个微妙的输出质量问题。在任何微分算法运行后,Git 会执行一个“压缩”阶段,通过移动和合并变更组,以产生更干净的输出。在某些情况下,这种移动可以使变更组穿越直方图算法选择的锚线,产生技术上正确但视觉上重复的差值。Git 现在能检测到这种情况并重新调整受影响区域,输出更紧凑,更符合预期。
[source] [ 来源 ]
…the rest of the iceberg
…冰山的其余部分
That’s just a sample of changes from the latest release. For more, check out the release notes for 2.53 and 2.54, or any previous version in the Git repository.
这只是最新版本中的一些变化示例。更多内容请查看 2.53 和 2.54 版本的发布说明,或 Git 仓库中任何之前版本 。
</details>