忙碌开发者的人工术 (Mánglù kāifā zhě de réngōng shù) Or, more naturally: 为忙碌开发者准备的人工术 (Wèi mánglù kāifā zhě zhǔnbèi de réngōng shù) (Both translate to something along the lines of "Jujutsu for Busy Developers" - the second is a bit more idiomatic.)
Jujutsu for busy devs

原始链接: https://maddie.wtf/posts/2025-07-21-jujutsu-for-busy-devs

## Jujutsu:一个简化的版本控制系统 Jujutsu (jj) 是一个建立在 Git 之上的新型版本控制系统,旨在提供更简单的思维模型和命令行体验,同时不牺牲功能。它提供堆叠差异、无缝变基和临时修订等特性。重要的是,它具有非破坏性——您只需一条命令即可在现有的 Git 仓库旁边开始使用它,并且可以随时恢复到 Git。 入门涉及安装 `jj` 工具、配置用户信息以及初始化仓库(现有仓库或克隆的仓库)。与 Git 以分支为中心的流程不同,Jujutsu 专注于 *修订版本*。您可以使用 `jj new` 在现有修订版本的基础上创建新的修订版本,编辑文件,然后使用 `jj describe` & `jj new`(或 `jj commit` 作为快捷方式)来最终确定更改。 导航简化了,可以使用 `jj edit` 命令来处理过去的修订版本,并使用 `jj bookmark` 管理“书签”(以前的分支)。使用 `jj git push` 将更改推送到远程仓库。Jujutsu 可以优雅地处理冲突,直接在文件中标记冲突以供解决。它旨在简化常见的 Git 任务,降低复杂性并改善开发人员体验。

## Hacker News 新闻 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 **为忙碌开发者提供的咒术 (maddie.wtf)** Bogdanp 发表于 40 分钟前,13 分 | 隐藏 | 过去 | 收藏 | 1 条评论 jackblemming 发表于 3 分钟前 [–] 我可能真的老了,因为我真的不觉得 Git 需要一个简化的模型。但如果人们觉得这有价值,那就祝他们好运。这篇博客写得也很好。回复 考虑申请 YC 2025 秋季批次!申请截止日期为 8 月 4 日。 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系方式 搜索:
相关文章

原文

Elevator Pitch

Jujutsu (jj) is a version control system with a significantly simplified mental model and command-line interface compared to Git, without sacrificing expressibility or power (in fact, you could argue Jujutsu is more powerful). Stacked-diff workflows, seamless rebases, and ephemeral revisions are all natural with jj, and it uses Git as a backend, which means you can begin using it non-destructively with a single command and can always drop back down to Git if you need to.

Getting Started

Install the jj command line tool. Make sure you set your authorship information, and also don't skip the section on setting up shell completions (the newer dynamic completions are well worth it).

jj config set --user user.name "Your Name"
jj config set --user user.email "[email protected]"

Then you'll need a repository to work with. You could use an existing repository of yours (although I'd recommend cloning a fresh copy, or at the very least making sure all your in-progress changes are committed and pushed), or if you want to follow along more closely, you can clone the repository containing this website's source code.

To use an existing repository, cd into it and run:

jj git init --colocate .

To clone a new one, you can do the clone and the jj initialisation in one step with (e.g.):

jj git clone --colocate [email protected]:maddiemort/maddie-wtf.git

In either case, you'll end up with a Jujutsu repository alongside the existing Git repository, in .jj/. This will not be visible to Git, and there's no risk of desynchronisation between the two: Jujutsu uses the Git repo as its storage backend, so whenever you interact with either jj or git, you're operating on the same data. The fact you're using Jujutsu rather than Git will not impact anyone else who interacts with the repository.

After initialising the colocated Git repo, jj will suggest that you track all of the remote branches that are associated with the ones you have checked out, and it helpfully provides a command that will track all of them at once (something like jj bookmark track main@origin, but containing each of the branch names that you have local checkouts of). Copy and paste it, and run it.

How To Use It

Jujutsu's command line interface has a much smaller surface area than Git, and its mental model is simpler. That means there's much less to learn—this should be less intimidating than Git was when you originally learned it—but it does mean you're going to have to adapt to a slightly different workflow.

You might also find Steve's Jujutsu Tutorial a helpful resource, but this page will try to provide a higher-level overview for busy devs who just want to get on with it.

Starting A New Revision

When you start working on something new with Git, the first thing you'd usually do is check out main, pull the latest version of it from the remote, and then create a new branch off from there. Jujutsu isn't as branch-oriented as Git is, so you don't start by creating a new branch—you just place a new revision on top of an existing one and start working inside it.

First, get the latest changes from the remote:

jj git fetch

Take a look at the repo history to orient yourself:

jj log

You should see something like this (if you're doing this in a fresh clone of a repo, this is probably all you'll see):

@  rxlwrvrr [email protected] 19 minutes ago 51931bfc(empty) (no description set)
  rttmyopz [email protected] 23 hours ago main git_head() 90ee7425
│  fix: add missed code tag around links to tags
~

In an attempt to be helpful, Jujutsu has done something that's actually a bit inconvenient for this tutorial: it created a new empty revision for you on top of main. Since in your typical workflow you would probably start out somewhere else in the history, this doesn't really fit with what I'm trying to teach—so for now, ignore that first entry.

The second entry is the important one. This is an entry in the log for a revision with ID rttmyopz, authored by [email protected] 23 hours ago, and it's got main pointing to it.

You'll also notice that there's a commit hash associated with this, 90ee7425, and that's the same one you'd see if you ran git log. The reason for the two different kinds of ID is that Jujutsu assigns an identifier to each revision, which it keeps stable even as the underlying Git commit might change (it will make more sense later why the Git hash might change).

Any of those identifiers can be used to refer to this revision (rttmyopz, or its highlighted prefix rt; 90ee7425, or its highlighted prefix 9; or main). Since, semantically, we want our new changes to be based on the current tip of main no matter what revision that is, let's use that identifier to create a new revision:

jj new main

Looking at the log reveals what happened:

@  ozpszrnr [email protected] 3 seconds ago c0f838b6(empty) (no description set)
  rttmyopz [email protected] 23 hours ago main git_head() 90ee7425
│  fix: add missed code tag around links to tags
~

A new revision has been created on top of the one at the tip of main, and it's been given the identifier ozpszrnr (whose shortest unique prefix is o, so I'll refer to it as such from now on). The @ on the left hand side marks that this is the working copy (current revision)—any changes we make are going to become part of this revision. We can use @ in commands to refer to whatever the current revision is at any given time.

You'll notice that the revision I told you to ignore earlier (rxlwrvrr) is gone. I'll explain why it disappeared in the next section. We could have just used that one, but I asked you to run jj new in order to demonstrate the full process of beginning a new set of changes on top of main.

Making Changes

As soon as you edit a file, the changes will be included in whatever revision you're currently editing—there's no separate staging area in Jujutsu. I'll make a change to README.md and then look at the log again.

@  ozpszrnr [email protected] 1 second ago 088e997b(no description set)
  rttmyopz [email protected] 1 day ago main git_head() 90ee7425
│  fix: add missed code tag around links to tags
~

A few things have changed. The revision is no longer marked as empty, because the change I just made is in it. If you're particularly sharp-eyed, you might notice that the Git commit hash immediately changed (but the Jujutsu revision ID remained the same). That's because as soon as changes were made, Jujutsu took care of keeping that synchronised with the underlying Git repo in the background by creating a new commit (you can git show it if you want!)—but those details are all abstracted away from us.

Let's say we're finished with this change and want to move on to the next revision (the same point at which you would have run git commit with Git). Jujutsu does have a jj commit command, but it's just a helper that combines two operations: a jj describe and a jj new. Doing the two steps individually will help build your mental model:

jj describe -m "chore: add a line to README.md"

The log now shows:

@  ozpszrnr [email protected] 2 seconds ago 0af545e5chore: add a line to README.md
  rttmyopz [email protected] 1 day ago main git_head() 90ee7425
│  fix: add missed code tag around links to tags
~

We've added a message to the revision, but we're still editing it (see the @ on the left?). To move on to the next revision, we can run jj new o (or just jj new, since it defaults to creating a new revision on top of the current revision).

@  kkrnnmxs [email protected] 1 second ago c50fbae5(empty) (no description set)ozpszrnr [email protected] 29 seconds ago git_head() 0af545e5
│  chore: add a line to README.md
  rttmyopz [email protected] 1 day ago main 90ee7425
│  fix: add missed code tag around links to tags
~

Hopefully now you can see why jj commit is just jj describe followed by jj new—it would have done exactly what we just did.

I'll take a moment for a brief interlude to teach you a few things about how to navigate around with Jujutsu.

  • Whenever you run jj new, Jujutsu creates a new empty revision—but if you ever move somewhere else and that revision is still empty and still has no description, it will be automatically abandoned. You therefore don't have to worry about littering your history with a bunch of empty revisions. You can try this right now in the tutorial with no ill effects—run jj new main, look at the log, then run jj new o again!
  • You can manually abandon a revision, even if it contains changes and/or has a description, with jj abandon <rev ID>. This is a destructive operation—it will abandon the rev without confirmation—but you can undo it with jj op undo. See the docs for the operation log if you want to know more about undoing.
  • As an alternative to creating new revisions, you can also move to inside an existing revision by running jj edit <rev ID>. If you were to run jj edit o at this point in the tutorial and check the log, you'd see an @ next to o, indicating it's the current revision (you're editing it). Then to get back to where you were, run jj new o again.
  • You can also refer to revisions in these commands using revset expressions rather than by their IDs. Some useful ones to know:
    • @ - the current revision
    • x- - the parent(s) of some revision x ("the revision before it" in the simple case)
    • y+ - the child(ren) of y ("the revision after it" in the simple case)
    • z:: - all descendants of z, including z

Branches (Bookmarks)

You'll have noticed that at no point so far did we ever think about creating a branch. That's because Jujutsu's relationship to branches is a bit different to Git's—they're just pointers that you move around so they point to whichever revision you want them to at a given time. There's no concept of "being on a branch" in Jujutsu, you're always just editing a revision.

In fact, Jujutsu's branches are different enough from Git's that they were recently renamed to "bookmarks" instead.

Let's say that our revision, o, contains the only changes we needed to make for this particular unit of work, and we're ready to create a PR. In order to share our changes, we'll need to create a bookmark:

jj bookmark create add-readme-line -r o

This command says "create a new bookmark called add-readme-line pointing to the revision o". The log reflects that:

@  kkrnnmxs [email protected] 3 minutes ago c50fbae5(empty) (no description set)ozpszrnr [email protected] 3 minutes ago add-readme-line git_head() 0af545e5
│  chore: add a line to README.md
  rttmyopz [email protected] 1 day ago main 90ee7425
│  fix: add missed code tag around links to tags
~

There's now an add-readme-line bookmark pointing to the revision o. If we want to push our changes, now we can:

jj git push -b add-readme-line --allow-new

The --allow-new arg tells Jujutsu it's okay to create a new branch on the remote (this confirmation is necessary because jj git push without a bookmark name will try to push all bookmarks that appear in the ancestors of the current revision).

The workflow from here on is the same as with Git—if you're using GitHub, the remote will reply with a message in your terminal containing a shortcut link straight to creating a PR with the new branch, just like usual.

Let's say you realise you need to add some more changes to your PR. I'll run through the first few commands all at once, since they're the same as above.

jj log

jj new o

<make some changes>

jj commit -m "chore: fix typo in README.md"

Here's the state of our repository now:

@  wlwmnxxn [email protected] 2 seconds ago 40959102(empty) (no description set)kkrnnmxs [email protected] 2 seconds ago git_head() 07602d4e
│  chore: fix typo in README.md
○  ozpszrnr [email protected] 5 minutes ago add-readme-line 0af545e5
│  chore: add a line to README.md
  rttmyopz [email protected] 1 day ago main 90ee7425
│  fix: add missed code tag around links to tags
~

Most of this won't be surprising, but one important thing to notice is that the bookmark didn't move—Jujutsu bookmarks don't automatically move when you commit, you just move them around yourself whenever they need to move. So let's do that:

jj bookmark move add-readme-line --to k

The log:

@  wlwmnxxn [email protected] 1 minute ago 40959102(empty) (no description set)kkrnnmxs [email protected] 1 minute ago add-readme-line* git_head() 07602d4e
│  chore: fix typo in README.md
○  ozpszrnr [email protected] 6 minutes ago add-readme-line@origin 0af545e5
│  chore: add a line to README.md
  rttmyopz [email protected] 1 day ago main 90ee7425
│  fix: add missed code tag around links to tags
~

The asterisk after the bookmark name indicates that the bookmark has diverged from the remote branch it's tracking (the remote branch is listed separately on the revision below as add-readme-line@origin). We can sort that out easily:

jj git push
@  wlwmnxxn [email protected] 1 minute ago 40959102(empty) (no description set)kkrnnmxs [email protected] 1 minute ago add-readme-line git_head() 07602d4e
│  chore: fix typo in README.md
○  ozpszrnr [email protected] 7 minutes ago 0af545e5
│  chore: add a line to README.md
  rttmyopz [email protected] 1 day ago main 90ee7425
│  fix: add missed code tag around links to tags
~

Conflicts

Jujutsu handles conflicts in a much less panicky way than Git does: whenever conflicts occur for any reason, it just marks the conflicted revisions and any of their children with a conflicted marker and then lets you continue with your day.

Inside those conflicted revisions, there will be conflict markers in the files that need them, and the way you resolve conflicts is simply to make the conflict markers go away by editing those files. You can do this in one of two ways: either directly edit a conflicted revision with jj edit <revision ID>, or create a new revision on top of the conflicted one with jj new <revision ID>, resolve the conflicts in that new revision, and then squash the resolution changes in with jj squash.


联系我们 contact @ memedata.com