diff --git a/skills/jujutsu/SKILL.md b/skills/jujutsu/SKILL.md new file mode 100644 index 0000000..e5f0411 --- /dev/null +++ b/skills/jujutsu/SKILL.md @@ -0,0 +1,205 @@ +--- +name: jujutsu +description: Use Jujutsu (jj) for version control in jj-enabled projects. Covers command workflows, bookmark management, rebasing, conflict resolution, revsets, and common pitfalls. Triggers when: project has a `.jj/` directory, README requires jj, or user explicitly requests jj commands. Do NOT use for regular git-only projects. +--- + +# Jujutsu Usage Guide + +Jujutsu (jj) is a next-generation VCS compatible with Git repositories but with fundamentally different usage patterns. + +## Mental Model (Fundamental Differences from Git) + +### Don't Think in Git Terms + +| Git Thinking | Problem | +|--------------|---------| +| `git add` | jj has no staging area, don't need add | +| Commits can't be changed | jj commits are editable "changes" | +| Use branches | jj uses bookmarks, lightweight pointers | +| `git stash` | Use `jj new @-` instead | +| Conflicts must be resolved immediately | jj conflicts can be deferred | + +### Correct Mental Model + +1. **Auto-tracking** — jj tracks all changes automatically, no `git add` needed +2. **Mutable commits** — commits are editable "changes", can be modified anytime +3. **Undoable operations** — almost all operations can be undone with `jj undo` +4. **Bookmarks** — lightweight pointers, similar to Git branches but with tracked concept +5. **Revsets** — powerful query syntax for complex conditions +6. **No checkout** — use `jj edit` to switch to a commit +7. **Non-blocking conflicts** — conflicts are recorded in commits, can be resolved later + +## Core Concepts + +### Change vs Commit + +- **Commit**: Snapshot of files + metadata (author, date, parents) +- **Change**: Evolution history of a commit, identified by change ID (similar to Gerrit's Change-Id) +- **Working-copy commit**: The commit corresponding to the current working directory (@ symbol) + +### Change ID vs Commit ID + +- **Change ID**: Unique to jj, 16 bytes randomly generated, format like `kntqzsqt`, remains unchanged +- **Commit ID**: Git-compatible commit hash, changes with content + +### Bookmark vs Branch + +- **Bookmark**: Named pointer to a commit, similar to Git branch +- **No "current bookmark"** — jj has no concept of active branch +- **Tracked bookmark**: Automatically tracks remote bookmark of the same name + +### Colocated Workspaces + +jj and git can coexist in the same directory: +- `.jj/` + `.git/` coexist +- jj and git commands can be mixed +- jj automatically imports/exports to git + +## Quick Command Reference + +```bash +# Status +jj st # Current changes (like git status) +jj log # History +jj log --graph # Graph view + +# Working with changes (no add needed) +jj diff # View current changes +jj describe # Edit commit message +jj commit -m "message" # Commit (auto-includes all changes) +jj squash # Squash into parent (like git commit --amend) +jj restore # Discard file changes + +# ⚠️ No checkout! +# Switch to edit a commit: jj edit +# Create new change: jj new + +# Creation and switching +jj new # Create new empty change (child of current @) +jj new # Create new change based on revision +jj new -b # Create new change and set bookmark +jj edit # Switch to a commit for editing (like checkout) + +# Bookmarks +jj bookmark list # List bookmarks +jj bookmark create -r # Create bookmark +jj bookmark delete # Delete bookmark +jj bookmark move --to # Move bookmark +jj bookmark track --remote= # Track remote bookmark + +# Rebase +jj rebase -b -o # Move entire branch +jj rebase -s -o # Move commit and descendants +jj rebase -r -o # Move only specified commit + +# Remote operations +jj git fetch # Fetch +jj git push # Push +jj git push --bookmark # Push specific bookmark + +# Undo +jj undo # Undo last operation +jj op log # View operation log + +# Multiple remotes +jj config set --user git.fetch '["upstream", "origin"]' +jj bookmark track main --remote=origin +``` + +## Common Workflows + +### Daily Commit +```bash +jj st +jj diff +jj commit -m "feat: add new feature" +jj log +``` + +### Modifying Historical Commits +```bash +# Edit current commit message +jj describe -m "new message" + +# Squash current changes into parent +jj squash + +# Squash into specific commit +jj squash --into +``` + +### Editing Existing Commit (like git checkout) +```bash +# Switch to a commit for editing — all subsequent changes amend this commit +jj edit +``` + +### Creating New Branch +```bash +jj new main -b topic +``` + +### Rebase +```bash +jj rebase -b topic -o main # Move entire branch (with all descendants) +jj rebase -s -o # Move single commit and descendants +jj rebase -r -o # Move only single commit (no descendants) +``` + +### Handling Conflicts +```bash +# Conflicts don't block; jj creates a conflicted change +jj new # Create new commit to resolve +# Edit conflict markers in files... +jj resolve # Mark resolved +jj squash # Squash into original +``` + +### Temporarily Stashing Work +```bash +jj new @- # Create sibling commit (like git stash) +jj edit # Restore +``` + +### Divergent Changes +```bash +jj log # Shows divergent label +jj abandon # Abandon one version +jj metaedit --update-change-id # Generate new change ID +jj squash --from --into # Merge both versions +``` + +## Important Notes + +1. **Don't use `git add`** — jj auto-tracks +2. **No checkout** — use `jj edit` to switch, `jj new` to create +3. **`jj commit` auto-includes all changes** — no `-a` needed +4. **Conflicts don't block** — can continue working, resolve later +5. **`jj new` creates a change** — editable empty commit, NOT a checkout +6. **Bookmarks have tracked concept** — like Git's upstream + +## Revset Quick Reference + +```bash +@ # Current working-copy commit +@- # Parent commit +root() # Root commit + +jj log -r ::@ # Ancestor chain of current commit +jj log -r 'all()' # All visible commits +jj log -r main.. # Commits after main branch +``` + +## Getting Help + +```bash +jj help +jj help +``` + +## Reference Files + +- **`references/commands.md`** — Full Git-to-jj command mapping table; read when looking up a specific command equivalent +- **`references/revsets.md`** — Complete revset query syntax and functions; read when writing `-r` expressions +- **`references/pitfalls.md`** — Common mistakes and pre-operation checklist; read when uncertain about correct approach +- **`references/advanced.md`** — Colocated workspaces, multiple remotes, divergent changes, filesets, operation log; read for advanced scenarios diff --git a/skills/jujutsu/references/advanced.md b/skills/jujutsu/references/advanced.md new file mode 100644 index 0000000..78289ee --- /dev/null +++ b/skills/jujutsu/references/advanced.md @@ -0,0 +1,263 @@ +# Advanced Topics + +> Supplementary content based on official docs + +## Colocated Workspaces + +jj and git can coexist in the same directory, facilitating migration and tool interoperability. + +### Creation + +```bash +# Create colocated workspace (default) +jj git init +# or +jj git clone + +# Disable colocation +jj git init --no-colocate +jj git clone --no-colocate +``` + +### Mixing jj and git + +```bash +# In colocated workspace you can: +jj st +git status # Can also use, but may show "detached HEAD" + +# jj commands auto import/export to git +# But recommend mainly using jj, git for read-only only +``` + +### Notes + +- jj commands frequently auto import/export, may cause branch conflicts +- In large repos jj can be slower (executes git import on every command) +- Git tools may have issues with conflicted files + +## Multiple Remotes + +### Typical Workflows + +#### Fork Workflow (Contributing Upstream) + +```bash +# 1. Configure fetch from multiple remotes +jj config set --user git.fetch '["upstream", "origin"]' + +# 2. Push only to origin +jj config set --user git.push origin + +# 3. Track remote bookmarks +jj bookmark track main # Track origin/main +jj bookmark track main --remote=upstream # Also track upstream + +# 4. Set trunk (as immutable base) +jj config set --user 'revset-aliases."trunk()"' main@upstream +``` + +#### Integration Workflow (Independent Repo) + +```bash +# 1. Only fetch and push from/to origin +jj config set --user git.fetch '["origin"]' + +# 2. Only track origin +jj bookmark track main --remote=origin +jj bookmark untrack main --remote=upstream + +# 3. Set trunk to origin +jj config set --user 'revset-aliases."trunk()"' main@origin +``` + +### Remote Bookmark References + +```bash +# Reference remote bookmarks +main@origin # main on origin +main@upstream # main on upstream + +# Create on new remote +jj new main@upstream +``` + +## Divergent Changes + +When a change ID has multiple visible commits. + +### Causes + +1. Local and remote both modified same change +2. Operating on same change from different workspaces +3. Concurrent operations + +### Identification + +```bash +jj log +# Shows: +# mzvwutvl/0 ... (divergent) +# mzvwutvl/1 ... (divergent) +``` + +### Resolution Strategies + +#### 1. Abandon One + +```bash +# Abandon unwanted version +jj abandon +``` + +#### 2. Generate New Change ID + +```bash +# Generate new change ID for a commit +jj metaedit --update-change-id +``` + +#### 3. Squash Together + +```bash +# Squash one into another +jj squash --from --into +``` + +#### 4. Ignore + +If not affecting work, can leave as-is. + +## Operation Log + +jj records every operation that modifies the repo, more powerful than Git's reflog. + +### Viewing + +```bash +# Operation list +jj op log + +# Operations with diffs +jj op log -p +``` + +### Undoing + +```bash +# Undo last operation +jj undo + +# Undo to specific operation +jj undo --at-operation +``` + +### Restoring to Previous State + +```bash +# Restore entire repo to state at operation +jj op restore --at-operation +``` + +### Time Travel + +```bash +# Run command at operation state (non-destructive) +jj --at-operation log +``` + +## Deep Dive on Conflicts + +### Conflict Types + +1. **File conflicts**: Same file/location modified differently +2. **Bookmark conflicts**: Local and remote bookmark move conflicts +3. **Change divergence**: Same change ID multiple visible commits + +### Conflict Resolution + +```bash +# 1. Create new commit on conflicted commit +jj new + +# 2. Edit files to resolve conflicts +# Edit conflict markers... + +# 3. Mark resolved +jj resolve + +# 4. If multiple conflicted files, after all resolved +jj squash +``` + +### Conflict Marker Styles + +Configurable (default "diff"): +```bash +# diff style (default) +jj config set --user ui.conflict-marker-style diff + +# snapshot style +jj config set --user ui.conflict-marker-style snapshot + +# git style +jj config set --user ui.conflict-marker-style git +``` + +## Filesets + +Similar to revsets but for file selection. + +### Syntax + +```bash +# File path +jj diff file.txt + +# glob pattern +jj diff 'glob:*.rs' + +# cwd prefix +jj diff 'cwd:src/' + +# root prefix +jj diff 'root:src/' + +# Combination +jj diff 'src ~ glob:**/test*.rs' +jj diff 'glob:*.rs | glob:*.md' +``` + +### Use Cases + +```bash +# Split only selected files +jj split 'glob:*.rs' + +# View specific directory diff +jj diff 'root:src/' +``` + +## Configuration Examples + +### User Configuration + +```toml +[user] +name = "Your Name" +email = "your@email.com" + +[ui] +color = "auto" +default-command = ["log", "--reversed"] + +[diff] +color-words.max-inline-alternation = 3 +``` + +### Auto-signing + +```toml +[signing] +behavior = "inline" +``` diff --git a/skills/jujutsu/references/commands.md b/skills/jujutsu/references/commands.md new file mode 100644 index 0000000..04e213c --- /dev/null +++ b/skills/jujutsu/references/commands.md @@ -0,0 +1,109 @@ +# Command Reference (with Explanations) + +> Based on official docs: https://www.jj-vcs.dev/latest/git-command-table/ + +## Basic Operations + +| Git | Jujutsu | Notes | +|-----|---------|-------| +| `git status` | `jj st` | View current state | +| `git diff` | `jj diff` | View uncommitted changes | +| `git diff HEAD` | `jj diff` | Same, jj compares to HEAD by default | +| `git diff ..` | `jj diff -r A..B` | Compare two commits | +| `git add` | ❌ Not needed | jj auto-tracks all changes | +| `git commit` | `jj commit -m "msg"` | Commit all current changes | +| `git commit -a` | `jj commit` | Same, jj doesn't need -a | +| `git commit --amend` | `jj squash` | Squash changes into parent | +| `git restore ` | `jj restore ` | Discard file changes | +| `git checkout -- ` | `jj restore ` | Same | + +## History Viewing + +| Git | Jujutsu | Notes | +|-----|---------|-------| +| `git log` | `jj log` | View history | +| `git log --oneline` | `jj log -r ::@` | Compact format | +| `git log --graph` | `jj log --graph` | Graph view | +| `git log --all` | `jj log -r 'all()'` | View all | +| `git show ` | `jj show ` | View commit details | +| `git blame ` | `jj file annotate ` | File annotation | + +## ⚠️ Branch Operations (Key Differences!) + +| Git | Jujutsu | Notes | +|-----|---------|-------| +| `git checkout ` | `jj edit ` | **Switch to edit commit** | +| `git checkout -b ` | `jj new -b ` | Create and set bookmark | +| `git switch ` | `jj new ` | Create new change | +| `git branch` | `jj bookmark list` | List | +| `git branch ` | `jj bookmark create ` | Create | +| `git branch -d ` | `jj bookmark delete ` | Delete | +| `git branch -f ` | `jj bookmark move --to ` | Move | + +**Key Points**: +- jj has no `jj co` or `jj checkout` commands! +- Use `jj edit ` to switch to a commit for editing +- Use `jj new` to create new changes (not switching!) + +## Rebase and Merge + +| Git | Jujutsu | Notes | +|-----|---------|-------| +| `git merge ` | `jj new @ A` | Merge (create new merge commit) | +| `git rebase A B` | `jj rebase -s A -o B` | A and descendants onto B | +| `git rebase --onto B A^ ` | `jj rebase -s A -o B` | Same | + +**Key Differences**: +- `-b` = Move entire branch (includes ancestors, excluding destination's ancestors) +- `-s` = Move specified commit and all descendants +- `-r` = Move only the specified commit (no descendants) + +## Remote Operations + +| Git | Jujutsu | Notes | +|-----|---------|-------| +| `git fetch` | `jj git fetch` | Fetch | +| `git pull` | `jj git fetch` (+ `jj new`) | | +| `git push` | `jj git push` | Push | +| `git push ` | `jj git push --bookmark ` | | +| `git remote add` | `jj git remote add` | | +| `git branch -u /` | `jj bookmark track --remote=` | Track remote | + +## Stashing and Undo + +| Git | Jujutsu | Notes | +|-----|---------|-------| +| `git stash` | `jj new @-` | Stash to sibling commit | +| `git stash pop` | `jj edit ` | Restore | +| `git reset --hard` | `jj abandon` | Abandon current change | +| `git reset --soft HEAD~` | `jj squash --from @-` | Keep changes | +| `git cherry-pick ` | `jj duplicate -o @` | Copy commit | + +## Undo Operations + +| Git | Jujutsu | Notes | +|-----|---------|-------| +| `git reflog` | `jj op log` | View operation log | +| `git reset --hard ` | `jj undo` | Undo last operation | + +**jj's `jj undo` is more powerful** — can undo almost any operation! + +## File Operations + +| Git | Jujutsu | Notes | +|-----|---------|-------| +| `git ls-files` | `jj file list` | List files | +| `git rm ` | `jj file delete ` | Delete | +| `git rm --cached ` | `jj file untrack ` | Untrack (must match ignore pattern) | +| `git rev-parse --show-toplevel` | `jj workspace root` | Repo root | + +## Advanced Operations + +| Git | Jujutsu | Notes | +|-----|---------|-------| +| `git add -p` | `jj split` | Interactive split | +| `git rebase -i` | `jj rebase -r` | Interactive rebase | +| | `jj absorb` | Auto-absorb changes into earlier commits | +| | `jj diffedit` | Interactive edit diff of a commit | +| | `jj describe` | Edit commit message | +| | `jj evolog` | View evolution history of a change | diff --git a/skills/jujutsu/references/pitfalls.md b/skills/jujutsu/references/pitfalls.md new file mode 100644 index 0000000..5c1feb0 --- /dev/null +++ b/skills/jujutsu/references/pitfalls.md @@ -0,0 +1,209 @@ +# Common Mistakes and Pitfalls + +> Based on official docs, mistakes agents commonly make. + +## 🔴 Critical Mistakes + +### Using `git add` + +**Mistake**: Using `git add` to stage files + +**Problem**: jj has no staging area, `git add` has no effect + +**Correct**: +```bash +# jj auto-tracks all changes, just commit directly +jj commit -m "message" + +# If you only want to commit some files, use jj split +jj split file1 file2 +``` + +### Using `jj co` or `jj checkout` to Switch + +**Mistake**: Using `jj co ` or `jj checkout` + +**Problem**: jj doesn't have these commands at all! + +**Correct**: +```bash +# Create new change on bookmark (like checkout -b) +jj new main + +# Create new change and set bookmark +jj new main -b myfeature + +# Edit existing commit +jj edit +``` + +### Using `git stash` + +**Mistake**: Using `git stash` + +**Problem**: jj has no stash, use `jj new @-` to create sibling commit + +**Correct**: +```bash +# Temporarily save current work (create sibling commit) +jj new @- + +# Restore: jj edit to go back to original commit +jj edit +``` + +### Using `git merge` + +**Mistake**: Using `jj merge` + +**Problem**: jj has no merge command + +**Correct**: +```bash +# Merge A into current commit +jj new @ A +``` + +## 🟠 Common Mistakes + +### Confusing `-b` and `-s` in Rebase + +**Misconception**: `-b` moves a single commit + +**Correct**: +- `-b `: Move entire branch (includes ancestors, excluding destination's ancestors) +- `-s `: Move commit and all descendants +- `-r `: Move only the specified commit (no descendants) + +```bash +# Move A and descendants +jj rebase -s A -o B + +# Move entire branch +jj rebase -b bookmark -o main + +# Move only single commit +jj rebase -r A -o B +``` + +### `jj new` Without Arguments + +**Misconception**: `jj new` equals `git checkout -b` + +**Correct**: `jj new` creates a new change based on current `@`, no bookmark name + +```bash +# Create new change (no bookmark) +jj new + +# Create and set bookmark +jj new -b + +# Create based on some commit +jj new +``` + +### Not Knowing How to Continue After Conflicts + +**Misconception**: Must resolve conflicts immediately + +**Correct**: jj allows continuing work first, resolving later + +```bash +# After conflict, jj creates conflicted change +# Can continue creating new commits +jj new + +# Then go back to resolve +jj new +# Resolve conflicts in files +jj resolve +jj squash +``` + +### Using `jj file untrack` Without Setting Ignore + +**Mistake**: Running `jj file untrack` directly + +**Problem**: File must match ignore pattern to untrack + +**Correct**: +```bash +# 1. Add to .gitignore first +echo "file.txt" >> .gitignore + +# 2. Then untrack +jj file untrack file.txt +``` + +### Confusing Bookmarks and Changes + +**Misconception**: Bookmark equals branch + +**Correct**: +- **Bookmark**: Similar to Git branch, pointer to a commit +- **Change**: jj's core concept, editable commit +- **Working-copy commit**: The commit in current working directory (@ symbol) +- **No "current bookmark"** — jj has no active branch concept + +### Ignoring Divergent Changes + +**Misconception**: Using change ID is always unambiguous + +**Correct**: If change ID has diverged, need to use commit ID or change ID with offset + +```bash +# Diverged change ID +jj log # Shows as xyz/0, xyz/1 + +# Use commit ID +jj edit + +# Or use change ID with offset +jj edit xyz/0 +``` + +## 🟡 Minor Issues + +### Forgetting `-m` Flag + +**Problem**: `jj commit` without `-m` opens editor + +**Suggestion**: Get in habit of using `jj commit -m "message"` + +### Using `git init` in Git Projects + +**Problem**: Should use `jj git init` + +**Correct**: +```bash +jj git init +# or +jj git clone +``` + +### Confusing Tracked vs Untracked Bookmarks + +**Misconception**: Auto-tracks after fetch + +**Correct**: +```bash +# Only tracks origin/main by default +# Others need manual track +jj bookmark track --remote= + +# View tracked +jj bookmark list --tracked +``` + +## Checklist + +Before operating, quick check: +- [ ] Don't use `git add` +- [ ] Don't use `jj co` or `jj checkout` +- [ ] Use `jj commit` not `git commit` +- [ ] Use `jj bookmark` not `git branch` +- [ ] Use `jj new @ A` not `git merge` +- [ ] Use `jj rebase -b`, `-s`, or `-r` for rebase +- [ ] Use `jj new @-` for stash, not `git stash` +- [ ] Diverged change IDs need explicit commit ID diff --git a/skills/jujutsu/references/revsets.md b/skills/jujutsu/references/revsets.md new file mode 100644 index 0000000..1de8d9b --- /dev/null +++ b/skills/jujutsu/references/revsets.md @@ -0,0 +1,135 @@ +# Revset Query Syntax + +> Based on official docs: https://www.jj-vcs.dev/latest/revsets/ + +Revsets are jj's powerful query syntax for precisely locating commits. + +## Basic Syntax + +```bash +jj log -r +``` + +## Common Symbols + +| Symbol | Meaning | +|--------|---------| +| `@` | Current working-copy commit | +| `@-` | Parent of current commit | +| `@--` | Grandparent commit | +| `main` | Commit pointed to by bookmark `main` | +| `HEAD` | Commit pointed to by HEAD | +| `root()` | Repository root commit (virtual commit, all-zero hash) | + +## Operators + +| Operator | Meaning | Example | +|----------|---------|---------| +| `::` | Ancestor range (inclusive) | `main::` = all descendants of main | +| `..` | Excluding ancestors | `main..main~5` | +| `\|` | Union | `main \| feature` | +| `&` | Intersection | `main & @` | +| `~` | Difference | `@ ~ main` | +| `-` | Parent (singular) | `@-` = parent commit | +| `+` | Child (singular) | | + +## Range Operators + +| Operator | Meaning | +|----------|---------| +| `x::y` | Descendants between x and y (inclusive) | +| `x..y` | Ancestors between x and y (excluding x's ancestors) | +| `::x` | All ancestors of x | +| `x::` | All descendants of x | + +**Note**: `..` on the left side does not distribute like `|`: +- `(A | B)..` = `A.. & B..` (intersection) +- `A.. | B..` = union + +## Functions + +| Function | Usage | Description | +|----------|-------|-------------| +| `all()` | `all()` | All visible commits | +| `none()` | `none()` | Empty set | +| `bookmarks()` | `bookmarks()` | All local bookmarks | +| `bookmarks(pattern)` | `bookmarks("main")` | Bookmarks matching pattern | +| `remote_bookmarks()` | `remote_bookmarks()` | All remote bookmarks | +| `visible_heads()` | `visible_heads()` | All visible heads | +| `parents(x)` | `parents(@)` | Parents of x | +| `children(x)` | `children(@)` | Children of x | +| `ancestors(x)` | `ancestors(@)` | Ancestors of x | +| `descendants(x)` | `descendants(@)` | Descendants of x | +| `first_parent(x)` | `first_parent(@)` | First parent only | +| `latest(x, n)` | `latest(@, 5)` | Most recent N commits | +| `merges()` | `merges()` | Merge commits | +| `file(path)` | `file("src/main.rs")` | Commits touching a file | +| `author(name)` | `author("yelo")` | Commits by author | +| `description(text)` | `description("feat")` | Commits with matching description | +| `date(expr)` | `date(2024-01-01)` | Commits on date | +| `empty()` | `empty()` | Commits with no file changes | +| `mutable()` | `mutable()` | Locally mutable commits | + +## Common Usage + +```bash +# History of current branch +jj log -r ::@ + +# All unpushed commits +jj log -r '@..@|bookmarks(@)..' + +# History of a bookmark +jj log -r main::main + +# Most recent 5 commits +jj log -r latest(@, 5) + +# Commits touching a file +jj log -r 'file(path/to/file)' + +# Commits after a date +jj log -r 'date(2024-01-01)..' + +# Commits by author +jj log -r 'author(yelo)' + +# Merge commits +jj log -r 'merges()' + +# Empty commits (no file changes) +jj log -r 'empty()' + +# Mutable commits (locally modified) +jj log -r 'mutable()' +``` + +## Shortcuts + +| Shorthand | Expands to | +|-----------|-----------| +| `@~n` | nth ancestor of @ | +| `@^` | `@-`, parent commit | +| `main~3` | 3rd ancestor of main | + +## Examples + +```bash +# View ancestor chain of current commit +jj log -r ::@ + +# View commits unique to feature branch +jj log -r 'feature - main' + +# View commits from the last week +jj log -r 'date(-7d)..' + +# View diff between two bookmarks +jj diff -r main..feature + +# View all commits by a specific author +jj log -r 'author(yelo)' + +# View latest position of all bookmarks +jj log -r 'bookmarks()' +```