Getting started with jj

Ellie Huxtable

開始使用 jj

過去幾週以來,我一直在用 jj!這不是我第一次嘗試它,但卻是第一次真正用上手。

多年來,我的 Git 工作流程都是在分支上「先做修改,然後執行 git commit --amend --no-edit && git push --force-with-lease」。這意味著我的變更能以極低的延遲同步到遠端。PR 通常只會有少少的幾個 commit,往往只有一個,我也會盡量讓它們保持精簡。

自從開始跟 AI 代理協作後,我發現自己切換分支的頻率變得高很多。因此,我要嘛得用 git stash,要嘛就得建一個有點蠢的 wip commit,來避免不同變更互相踩到。

這時我們的新英雄 jj 登場了!這裡的模型有點不一樣。我不會寫一份長篇大論的手冊,因為網路上已經有很多了,而且都比我隨手胡亂記下的筆記好得多。

簡單來說,最吸引我的地方在於,我的變更永遠都是已提交的狀態。每次編輯都會自動修正(amend)最近的一次 commit,而推送則會覆寫遠端(或者用 jj 的說法,就是把它「橫向移動」)。

編輯一個變更非常簡單,不需要搞什麼 checkout/rebase 的繁瑣操作。感覺就像有人把我偏好的 Git 工作流程拿去,直接圍繞它打造了一個工具。我也不太確定自己為什麼花了這麼久才搞懂這件事。

這真的就只是隨手寫下的腦內整理。之後學到更多東西時,我應該會再發表更多筆記。

工作流程

要進行一項變更時,我的工作流程是

jj new main

<do some coding>

jj describe -m 'feat: something really cool'
jj git push -c @

就這樣。合併 PR,然後重複。

在 jj 裡,我們談論的是 change 而非 commit。一個 change 就是一個不斷演進的 commit。而「@」指的就是目前的 change

開始一個新的變更

在開始工作之前,你應該先建立一個變更。你接下來要做的所有編輯都會存在這裡

jj new main

這會在 main(也就是帶有 main 書籤的那個 commit)之上建立你的 commit(剛接觸時,change 和 commit 的區別是真實存在且令人困惑的)。如果你想建立一個以目前所在位置為父節點的新變更,只要執行 jj new 就好。

為變更加上說明

你可能已經習慣先把一些變更加入暫存區(staging area),再用一段訊息提交。但在 jj 裡,你的變更已經是加入完成的狀態了。不過你的變更可能還沒有說明(description)!(就像 commit 訊息一樣)

jj describe -m 'hello i am a message'

你也可以在建立變更的同時就加上說明,像這樣:

jj new -m "hello i am a message"

將 commit 推送到新分支

如果你正處於某個變更上,對它感到滿意,並想把它放到 GitHub 上讓某個 agent 來狠狠吐槽你(其實是 Claude)的程式碼?

很簡單。

jj git push -c @

也就是「用 Git 來推送目前的 commit」的意思。它會自動建立一個分支,並推送到遠端。你其實可以在修改過程中一再執行這個指令,它會自動把 jj 的魔法轉換成 Git 的 amend 和 force push。

不過,我很期待 jj 未來能擁有自己的協定與託管服務。

編輯 commit

如果想跳回去編輯某個已有的變更?很簡單

jj edit <change-id>

對我來說,從這裡開始就能感受到它的強大之處,同時也是我原本試圖套用的心智模型開始崩解的地方。這裡不需要搞什麼 checkout、rebase 之類的麻煩事。

jj 同時有 changes 和 commits。如果我們記得它是持續演進一個 commit,而不是有個暫存區,那麼就可以把 change 想成是穩定的 ID,而 commit ID 則是會隨著每次編輯而改變的 ID

復原操作

你在 jj 裡做的幾乎任何事都會被記錄在操作紀錄(op log)中。如果做了某件事結果搞砸了?jj undo 就能救回來。

更多 Git 相關操作

jj 目前在很大程度上還是依附在 Git 之上。他們已經讓轉換現有儲存庫變得很容易,也能與 GitHub 整合

# Create a jj repo inside of a git repo
jj git init

# Fetch from the remote (github etc)
jj git fetch

# Push
jj git push

# Clone
jj git clone

好,那分支在哪裡?

沒有分支!至少不是你習慣的那種。jj 使用的是「bookmarks」,也就是你可以附加到某個 commit 上的字串。當你推送一個變更時,它會自動幫你建立一個書籤——例如 push-refrefref

# bookmark the current change with a name
jj bookmark create <name>

# move a bookmark to a new commit
jj bookmark set <name> -r <commit>

# other things exist just use --help

我目前最感到困擾的是,建立新變更時,書籤不會跟著移動。

引自詞彙表

與 Git 不同,這裡沒有「目前書籤」的概念;當你建立新的 commit 時,書籤不會移動。如果 commit 被重寫,書籤則會自動跟著它移動。

之後我再來寫些關於 rebase、編輯和其他有趣功能的東西


註:用 Vim 寫這篇文章超煩的,因為我把 jj 綁定成了 <esc>

原文由 Ellie Huxtable 發布

本文章由 muse-spark-1.2-contributor 進行翻譯