git-absorb: git commit --fixup, but automatic
git-absorb: git commit —fixup, but automatic
git-absorb is a port of Facebook’s hg absorb, which I first read about on mozilla.dev.version-control: “Facebook demoed hg absorb which is probably the coolest workflow enhancement I’ve seen to version control in years.” Essentially, when your working directory has uncommitted changes on top of draft changesets, you can run hg absorb and the uncommitted modifications are automagically folded (“absorbed”) into the appropriate draft ancestor changesets. This is essentially doing hg histedit + “roll” actions without having to make a commit or manually make history modification rules. The command essentially looks at the lines that were modified, finds a changeset modifying those lines, and amends that changeset to include your uncommitted changes. If the changes can’t be made without conflicts, they remain uncommitted. This workflow is insanely useful for things like applying review feedback. You just make file changes, run hg absorb and the mapping of changes to commits sorts itself out. It is magical.
git-absorb 是 Facebook hg absorb 的移植版本,我最初是在 mozilla.dev.version-control 上读到它的:“Facebook 演示了 hg absorb,这可能是我多年来见过的最酷的版本控制工作流增强功能。” 从本质上讲,当你的工作目录在草稿变更集(draft changesets)之上还有未提交的更改时,你可以运行 hg absorb,这些未提交的修改会自动地(“神奇地”)折叠(“吸收”)到相应的草稿祖先变更集中。这实际上相当于执行了 hg histedit + “roll” 操作,而无需手动创建提交或手动制定历史修改规则。该命令会查看被修改的行,找到修改这些行的变更集,并修改该变更集以包含你的未提交更改。如果更改无法在没有冲突的情况下完成,它们将保持未提交状态。这种工作流对于应用审查反馈等任务非常有用。你只需修改文件,运行 hg absorb,更改与提交之间的映射就会自动理顺。这简直是魔法。
Elevator Pitch
You have a feature branch with a few commits. Your teammate reviewed the branch and pointed out a few bugs. You have fixes for the bugs, but you don’t want to shove them all into an opaque commit that says “fixes,” because you believe in atomic commits. Instead of manually finding commit SHAs for git commit --fixup, or running a manual interactive rebase, do this:
git add $FILES_YOU_FIXED
git absorb --and-rebase
电梯演讲(核心卖点)
假设你有一个包含几个提交的功能分支。你的队友审查了该分支并指出了几个 Bug。你修复了这些 Bug,但你不想把它们全部塞进一个写着“fixes”的模糊提交中,因为你推崇原子提交。与其手动查找 git commit --fixup 所需的提交 SHA,或者手动运行交互式变基(rebase),不如这样做:
git add $FILES_YOU_FIXED
git absorb --and-rebase
git absorb will automatically identify which commits are safe to modify, and which staged changes belong to each of those commits. It will then write fixup! commits for each of those changes. With the --and-rebase flag, these fixup commits will be automatically integrated into the corresponding ones. Alternatively, you can check its output manually if you don’t trust it, and then fold the fixups into your feature branch with git’s built-in autosquash functionality:
git add $FILES_YOU_FIXED
git absorb
git log # check the auto-generated fixup commits
git rebase -i --autosquash master
git absorb 会自动识别哪些提交可以安全修改,以及哪些暂存的更改属于这些提交。然后,它会为每个更改编写 fixup! 提交。使用 --and-rebase 标志,这些 fixup 提交将自动集成到相应的提交中。或者,如果你不放心,也可以手动检查输出,然后使用 git 内置的 autosquash 功能将 fixup 折叠到你的功能分支中:
git add $FILES_YOU_FIXED
git absorb
git log # 检查自动生成的 fixup 提交
git rebase -i --autosquash master
Installing
The easiest way to install git absorb is to download an artifact from the latest tagged release. Artifacts are available for Windows, MacOS, and Linux (built on Ubuntu with statically linked libgit2). If you need a commit that hasn’t been released yet, check the latest CI artifact or file an issue. Alternatively, git absorb is available in the following system package managers:
安装
安装 git absorb 最简单的方法是从最新的标记版本下载构建产物。这些产物适用于 Windows、MacOS 和 Linux(在 Ubuntu 上构建,并静态链接了 libgit2)。如果你需要尚未发布的提交,请查看最新的 CI 构建产物或提交 Issue。此外,git absorb 也可通过以下系统包管理器获取:
| Repository | Command |
|---|---|
| Arch Linux | pacman -S git-absorb |
| Debian | apt install git-absorb |
| DPorts | pkg install git-absorb |
| Fedora | dnf install git-absorb |
| Flox | flox install git-absorb |
| FreeBSD Ports | pkg install git-absorb |
| Homebrew and Linuxbrew | brew install git-absorb |
| MacPorts | sudo port install git-absorb |
| nixpkgs stable and unstable | nix-env -iA nixpkgs.git-absorb |
| openSUSE | zypper install git-absorb |
| Ubuntu | apt install git-absorb |
| Void Linux | xbps-install -S git-absorb |
| GNU Guix | guix install git-absorb |
| Windows Package Manager | winget install tummychow.git-absorb |
Compiling from Source
You will need the following: cargo. Then cargo install git-absorb. Make sure that $CARGO_HOME/bin is on your $PATH so that git can find the command. ($CARGO_HOME defaults to ~/.cargo.) Note that git absorb does not use the system libgit2. This means you do not need to have libgit2 installed to build or run it. However, this does mean you have to be able to build libgit2. (Due to recent changes in the git2 crate, CMake is no longer needed to build it.)
从源码编译
你需要安装 cargo,然后运行 cargo install git-absorb。请确保 $CARGO_HOME/bin 在你的 $PATH 中,以便 git 能找到该命令。($CARGO_HOME 默认为 ~/.cargo。)注意,git absorb 不使用系统级的 libgit2。这意味着你不需要安装 libgit2 即可构建或运行它。但是,这确实意味着你需要具备构建 libgit2 的环境。(由于 git2 crate 的近期更改,现在构建它已不再需要 CMake。)
Note: cargo install does not currently know how to install manpages (cargo#2729), so if you use cargo for installation then git absorb --help will not work. There are two manual workarounds, assuming your system has a ~/.local/share/man/man1 directory that man --path knows about:
注意:cargo install 目前无法安装 man 手册页(cargo#2729),因此如果你使用 cargo 安装,git absorb --help 将无法工作。假设你的系统拥有 man --path 可识别的 ~/.local/share/man/man1 目录,有两种手动解决方法:
-
Build the man page from source and copy: This requires that the
a2xtool be installed on your system.cd ./Documentationmakemv git-absorb.1 ~/.local/share/man/man1 -
Download a recently-built man page: Find a recent build as in “Installing” above, download the
git-absorb.1file and unzip, then move it to~/.local/share/man/man1. -
从源码构建并复制 man 手册页:这需要你的系统安装了
a2x工具。cd ./Documentationmakemv git-absorb.1 ~/.local/share/man/man1 -
下载最近构建的 man 手册页:按照上述“安装”部分找到最近的构建版本,下载
git-absorb.1文件并解压,然后将其移动到~/.local/share/man/man1。
Usage
git add any changes that you want to absorb. By design, git absorb will only consider content in the git index (staging area).
git absorb
This will create a sequence of commits on HEAD. Each commit will have a fixup! message indicating the message (if unique) or SHA of the commit it should be squashed into. If you are satisfied with the output, git rebase -i --autosquash to squash the fixup! commits into their predecessors. You can set the GIT_SEQUENCE_EDITOR environment variable if you don’t need to edit the rebase TODO file.
使用方法
使用 git add 添加你想要吸收的任何更改。按照设计,git absorb 只会考虑 git 索引(暂存区)中的内容。
git absorb
这将在 HEAD 上创建一系列提交。每个提交都会有一个 fixup! 消息,指明它应该被压缩进哪个提交(通过消息或 SHA)。如果你对输出满意,运行 git rebase -i --autosquash 将 fixup! 提交压缩到它们的前驱提交中。如果你不需要编辑 rebase TODO 文件,可以设置 GIT_SEQUENCE_EDITOR 环境变量。
If you are not satisfied (or if something bad happened), git reset --soft PRE_ABSORB_HEAD will reset to the pre-absorption commit and recover your old state. (You can also find the commit in question with git reflog.) And if you think git absorb is at fault, please file an issue.
如果你不满意(或者发生了意外),git reset --soft PRE_ABSORB_HEAD 将重置到吸收前的提交并恢复你的旧状态。(你也可以通过 git reflog 找到相关的提交。)如果你认为这是 git absorb 的问题,请提交 Issue。
How it works (roughly)
git absorb works by checking if two patches P1 and P2 commute, that is, if applying P1 before P2 gives the same result as applying P2 before P1. git absorb considers a range of commits ending at HEAD. The first commit can be specified explicitly with --base <ref>. By default the last 10 commits will be considered (see STACK SIZE in git-absorb.adoc for how to change this).
工作原理(简述)
git absorb 的工作原理是检查两个补丁 P1 和 P2 是否可交换,即先应用 P1 再应用 P2 的结果是否与先应用 P2 再应用 P1 的结果相同。git absorb 会考虑以 HEAD 结尾的一系列提交。第一个提交可以通过 --base <ref> 显式指定。默认情况下,它会考虑最近的 10 个提交(有关如何更改此设置,请参阅 git-absorb.adoc 中的 STACK SIZE)。
For each hunk in the index, git absorb will check if that hunk commutes with the last commit, then the one before that, etc. When it finds a commit that does not commute with the hunk, it infers that this is the right parent commit for this change, and the hunk is turned into a fixup commit. If the hunk commutes with all commits in the range, it means we have not found a suitable parent commit for this change; a warning is displayed, and this hunk remains uncommitted in the index.
对于索引中的每个代码块(hunk),git absorb 会检查该代码块是否与最后一个提交可交换,然后是倒数第二个,依此类推。当它找到一个与该代码块不可交换的提交时,它会推断这就是该更改的正确父提交,并将该代码块转换为一个 fixup 提交。如果该代码块与范围内的所有提交都可交换,则意味着我们没有为该更改找到合适的父提交;此时会显示警告,并且该代码块将保持未提交状态留在索引中。
Documentation
For additional information about git-absorb, including all arguments and configuration options, see Documentation/git-absorb.adoc, or the git-absorb manual page.
文档
有关 git-absorb 的更多信息,包括所有参数和配置选项,请参阅 Documentation/git-absorb.adoc 或 git-absorb 手册页。
TODO
- implement remote default branch check
- stop using failure::err
待办事项
- 实现远程默认分支检查
- 停止使用 failure::err