just playing with tangled
at gvimdiff 589 lines 24 kB view raw view rendered
1# Frequently asked questions 2 3### Why does my bookmark not move to the new commit after `jj new/commit`? 4 5If you're familiar with Git, you might expect the current bookmark to move forward 6when you commit. However, Jujutsu does not have a concept of a "current bookmark". 7 8To move bookmarks, use `jj bookmark move`. 9 10### I made a commit and `jj git push --all` says "Nothing changed" instead of pushing it. What do I do? 11 12`jj git push --all` pushes all _bookmarks_, not all revisions. You have two 13options: 14 15* Using `jj git push --change` will automatically create a bookmark and push it. 16* Using `jj bookmark` commands to create or move a bookmark to either the commit 17 you want to push or a descendant on it. Unlike Git, Jujutsu doesn't do this 18 automatically (see previous question). 19 20### Where is my commit, why is it not visible in `jj log`? 21 22Is your commit visible with `jj log -r 'all()'`? 23 24If yes, you should be aware that `jj log` only shows a subset of the commits in 25the repo by default. Most commits that exist on a remote are not shown. Local 26commits and their immediate parents (for context) are shown. The thinking is 27that you are more likely to interact with this set of commits. You can configure 28the set of revisions to show by default by overriding `revsets.log` as described 29in [config]. 30 31If not, the revision may have been abandoned (e.g. because you 32used `jj abandon`, or because it's an obsolete version that's been rewritten 33with `jj rebase`, `jj describe`, etc). In that case, `jj log -r commit_id` 34should show the revision as "hidden". `jj new commit_id` should make the 35revision visible again. 36 37See [revsets] and [templates] for further guidance. 38 39### What are elided revisions in the output of `jj log`? How can I display them? 40 41"Elided revisions" appears in the log when one revision descends from another, 42both are in the revset, but the revisions connecting them are _not_ in the 43revset. 44 45For example, suppose you log the revset `tyl|mus` which contains exactly two 46revisions: 47 48```sh 49$ jj log -r 'tyl|mus' 50○ musnqzvt me@example.com 1 minute ago 9a09f8a5 51│ Revision C 52~ (elided revisions) 53○ tylynnzk me@example.com 1 minute ago f26967c8 54│ Revision A 55``` 56 57Only the two revisions in the revset are displayed. The text "(elided 58revisions)" is shown to indicate that `musnqzvt` descends from `tylynnzk`, but 59the nodes connecting them are not in the revset. 60 61To view the elided revisions, change the [revset expression](revsets.md) so it 62includes the connecting revisions. The `connected()` revset function does 63exactly this: 64 65```sh 66$ jj log -r 'connected(tyl|mus)' 67○ musnqzvt me@example.com 43 seconds ago 9a09f8a5 68│ Revision C 69○ rsvnrznr me@example.com 43 seconds ago 5b490f30 70│ Revision B 71○ tylynnzk me@example.com 43 seconds ago f26967c8 72│ Revision A 73``` 74 75### How can I get `jj log` to show me what `git log` would show me? 76 77Use `jj log -r ..`. The `..` [operator] lists all visible commits in the repo, excluding the root (which is never interesting and is shared by all repos). 78 79### Can I monitor how `jj log` evolves? 80 81The simplest way to monitor how the history as shown by `jj log` evolves is by using the [watch(1)](https://man7.org/linux/man-pages/man1/watch.1.html) command (or [hwatch](https://github.com/blacknon/hwatch?tab=readme-ov-file#configuration) or [viddy](https://github.com/sachaos/viddy)). 82For example: 83 84```sh 85watch --color jj --ignore-working-copy log --color=always 86``` 87 88This will continuously update the (colored) log output in the terminal. 89The `--ignore-working-copy` option avoids conflicts with manual operations during the creation of snapshots. 90Martin used watch in a [tmux](https://github.com/tmux/tmux/wiki) pane during his presentation [Jujutsu - A Git-compatible VCS](https://www.youtube.com/watch?v=LV0JzI8IcCY). 91 92Alternatively, you can use [jj-fzf](https://github.com/tim-janik/jj-fzf), where the central piece is the `jj log` view and common operations can be carried out via key bindings while the log view updates. 93 94The wiki lists additional TUIs and GUIs beyond the terminal: [GUI-and-TUI](https://github.com/jj-vcs/jj/wiki/GUI-and-TUI) 95 96### Should I co-locate my repository? 97 98Co-locating a Jujutsu repository allows you to use both Jujutsu and Git in the 99same working copy. The benefits of doing so are: 100 101- You can use Git commands when you're not sure how to do something with 102 Jujutsu, Jujutsu hasn't yet implemented a feature (e.g., bisection), or you 103 simply prefer Git in some situations. 104 105- Tooling that expects a Git repository still works (IDEs, build tooling, etc.) 106 107The [co-location documentation describes the 108drawbacks](git-compatibility.md#co-located-jujutsugit-repos) but the most 109important ones are: 110 111- Interleaving `git` and `jj` commands may create confusing bookmark conflicts 112 or divergent changes. 113 114- If the working copy commit or its parent contain any conflicted files, tools 115 expecting a Git repo may interpret the commit contents or its diff in a wrong 116 and confusing way. You should avoid doing mutating operations with Git tools 117 and ignore the confusing information such tools present for conflicted commits 118 (unless you are curious about [the details of how `jj` stores 119 conflicts](technical/conflicts.md)). See 120 [\#3979](https://github.com/jj-vcs/jj/issues/3979) for plans to improve 121 this situation. 122 123- Jujutsu commands may be a little slower in very large repositories due to 124 importing and exporting changes to Git. Most repositories are not noticeably 125 affected by this. 126 127If you primarily use Jujutsu to modify the repository, the drawbacks are 128unlikely to affect you. Try co-locating while you learn Jujutsu, then switch if 129you find a specific reason not to co-locate. 130 131### `jj` is said to record the working copy after `jj log` and every other command. Where can I see these automatic "saves"? 132 133Indeed, every `jj` command updates the current "working-copy" revision, marked 134with `@` in `jj log`. You can notice this by how the [commit ID] of the 135working copy revision changes when it's updated. Note that, unless you move to 136another revision (with `jj new` or `jj edit`, for example), the [change ID] will 137not change. 138 139If you expected to see a historical view of your working copy changes in the 140parent-child relationships between commits you can see in `jj log`, this is 141simply not what they mean. What you can see in `jj log` is that after the 142working copy commit gets amended (after any edit), the commit ID changes. 143 144You can see the actual history of working copy changes using `jj evolog`. This 145will show the history of the commits that were previously the "working-copy 146commit", since the last time the change id of the working copy commit changed. 147The obsolete changes will be marked as "hidden". They are still accessible with 148any `jj` command (`jj diff`, for example), but you will need to use the commit 149id to refer to hidden commits. 150 151You can also use `jj evolog -r` on revisions that were previously the 152working-copy revisions (or on any other revisions). Use `jj evolog -p` as an 153easy way to see the evolution of the commit's contents. 154 155### Can I prevent Jujutsu from recording my unfinished work? I'm not ready to commit it. 156 157Jujutsu automatically records new files in the current working-copy commit and 158doesn't provide a way to prevent that. 159 160However, you can easily record intermediate drafts of your work. If you think 161you might want to go back to the current state of the working-copy commit, 162simply use `jj new`. There's no need for the commit to be "finished" or even 163have a description. 164 165Then future edits will go into a new working-copy commit on top of the now 166former working-copy commit. Whenever you are happy with another set of edits, 167use `jj squash` to amend the previous commit. 168 169If you have changes you _never_ want to put in a public commit, see: [How can I 170keep my scratch files in the repository without committing 171them?](#how-can-i-keep-my-scratch-files-in-the-repository-without-committing-them) 172 173For more options see the next question. 174 175### Can I interactively create a new commit from only some of the changes in the working copy, like `git add -p && git commit` or `hg commit -i`? 176 177Since the changes are already in the working-copy commit, the equivalent to 178`git add -p && git commit`/`git commit -p`/`hg commit -i` is to split the 179working-copy commit with `jj split -i` (or the practically identical 180`jj commit -i`). 181 182For the equivalent of `git commit --amend -p`/`hg amend -i`, use `jj squash -i`. 183 184### Is there something like `git rebase --interactive` or `hg histedit`? 185 186It is often sufficient to use some form of `jj rebase` with `-A/-B`. For 187example, if you have a linear chain of revisions `A` through `C` and want to 188move `C` before `B`, use `jj rebase -r C -B B`. See `jj help rebase` for more 189examples. 190 191There is a [tracking issue][reordering] you can check for updates. 192 193To squash or split commits, use `jj squash` and `jj split`. 194 195### How can I keep my scratch files in the repository without committing them? 196 197You can set `snapshot.auto-track` to only start tracking new files matching the 198configured pattern (e.g. `"none()"`). Changes to already tracked files will 199still be snapshotted by every command. 200 201You can keep your notes and other scratch files in the repository, if you add 202a wildcard pattern to either the repo's `gitignore` or your global `gitignore`. 203Something like `*.scratch` or `*.scratchpad` should do, after that rename the 204files you want to keep around to match the pattern. 205 206If you keep your scratch files in their own directory with no tracked files, you 207can create a `.gitignore` file in that directory containing only `*`. This will 208ignore everything in the directory including the `.gitignore` file itself. 209 210If `$EDITOR` integration is important, something like `scratchpad.*` may be more 211helpful, as you can keep the filename extension intact (it 212matches `scratchpad.md`, `scratchpad.rs` and more). Another option is to add a 213directory to the global `.gitignore` which then stores all your temporary files 214and notes. For example, you could add `scratch/` to `~/.git/ignore` and then 215store arbitrary files in `<your-git-repo>/scratch/`. 216 217You can find more details on `gitignore` files [here][gitignore]. 218 219### How can I avoid committing my local-only changes to tracked files? 220 221Suppose your repository tracks a file like `secret_config.json`, and you make 222some changes to that file to work locally. Since Jujutsu automatically commits 223the working copy, there's no way to prevent Jujutsu from committing changes to 224the file. But, you never want to push those changes to the remote repository. 225 226One solution is to keep these changes in a separate commit branched from the 227trunk. To use those changes in your working copy, _merge_ the private commit 228into your branch. 229 230Suppose you have a commit "Add new feature": 231 232```shell 233$ jj log 234@ xxxxxxxx me@example.com 2024-08-21 11:13:21 ef612875 235│ Add new feature 236◉ yyyyyyyy me@example.com 2024-08-21 11:13:09 main b624cf12 237│ Existing work 238~ 239``` 240 241First, create a new commit branched from main and add your private changes: 242 243```shell 244$ jj new main -m "private: my credentials" 245Working copy (@) now at: wwwwwwww 861de9eb (empty) private: my credentials 246Parent commit (@-) : yyyyyyyy b624cf12 main | Existing work 247Added 0 files, modified 1 files, removed 0 files 248 249$ echo '{ "password": "p@ssw0rd1" }' > secret_config.json 250``` 251 252Now create a merge commit with the branch you're working on and the private 253commit: 254 255```shell 256$ jj new xxxxxxxx wwwwwwww 257Working copy (@) now at: vvvvvvvv ac4d9fbe (empty) (no description set) 258Parent commit (@-) : xxxxxxxx ef612875 Add new feature 259Parent commit (@-) : wwwwwwww 2106921e private: my credentials 260Added 0 files, modified 1 files, removed 0 files 261 262$ jj log 263@ vvvvvvvv me@example.com 2024-08-22 08:57:40 ac4d9fbe 264├─╮ (empty) (no description set) 265│ ◉ wwwwwwww me@example.com 2024-08-22 08:57:40 2106921e 266│ │ private: my credentials 267◉ │ xxxxxxxx me@example.com 2024-08-21 11:13:21 ef612875 268├─╯ Add new feature 269◉ yyyyyyyy me@example.com 2024-08-21 11:13:09 main b624cf12 270│ Existing work 271~ 272``` 273 274Now you're ready to work: 275 276- Your work in progress _xxxxxxxx_ is the first parent of the merge commit. 277- The private commit _wwwwwwww_ is the second parent of the merge commit. 278- The working copy (_vvvvvvvv_) contains changes from both. 279 280As you work, squash your changes using `jj squash --into xxxxxxxx`. 281 282If you need a new empty commit on top of `xxxxxxxx` you can use the 283`--insert-after` and `--insert-before` options (`-A` and `-B` for short): 284 285```shell 286# Insert a new commit after xxxxxxxx 287$ jj new --no-edit -A xxxxxxxx -m "Another feature" 288Working copy (@) now at: uuuuuuuu 1c3cff09 (empty) Another feature 289Parent commit (@-) : xxxxxxxx ef612875 Add new feature 290 291# Insert a new commit between yyyyyyyy and vvvvvvvv 292$ jj new --no-edit -A yyyyyyyy -B vvvvvvvv -m "Yet another feature" 293Working copy (@) now at: tttttttt 938ab831 (empty) Yet another feature 294Parent commit (@-) : yyyyyyyy b624cf12 Existing work 295``` 296 297To avoid pushing change _wwwwwwww_ by mistake, use the configuration 298[git.private-commits](config.md#set-of-private-commits): 299 300```shell 301jj config set --user git.private-commits "'''description(glob:'private:*')'''" 302``` 303 304### I accidentally changed files in the wrong commit, how do I move the recent changes into another commit? 305 306Let's say we are editing a commit for "featureA", and we forgot to run `jj 307new` or `jj commit` before doing some work that belongs in a new commit: 308 309```console 310$ jj log 311@ lnvvtrzo jjfan@example.org 2025-02-28 21:01:10 31a347e0 312│ featureA 313◆ zzzzzzzz root() 00000000 314$ cat file # Oh no, the work on "feature B" should be in a separate commit! 315Done with feature A 316Working on feature B 317``` 318 319#### Step 1: Find the commit id for the "last good version" 320 321<!-- TODO: Reorganize the two related questions, this one and 322 -- the one linked below 323 --> 324You can find [all the past versions of the working copy revision that `jj` has 325saved](#jj-is-said-to-record-the-working-copy-after-jj-log-and-every-other-command-where-can-i-see-these-automatic-saves) 326by running `jj evolog`. The obsolete versions will be marked as "hidden" and 327will have the same change id, but will have different commit ids. This 328represents different [commits] that are all parts of the same [change]. 329 330For example, this is what the evolog might look like after you made two edits to 331the same change: 332 333```console 334$ # Note the word "hidden", the commit ids on the right, 335$ # and the unchanging change id on the left. 336$ jj evolog 337@ lnvvtrzo jjfan@example.org 2025-02-28 21:01:10 31a347e0 338│ featureA 339○ lnvvtrzo hidden jjfan@example.org 2025-02-28 21:00:51 b8004ab8 340│ featureA 341○ lnvvtrzo hidden jjfan@example.org 2025-02-28 20:50:05 e4d831d 342 (no description set) 343``` 344 345Since commit `b800` is hidden, it is considered obsolete and `jj log` (without 346arguments) will not show it, nor can it be accessed by its change id. However, 347most `jj` operations work normally on such commits if you refer to them by their 348commit id. 349 350To find out which of these versions is the last time before we started working 351on feature B (the point where we should have created a new change, but failed 352to do so), we can look at the actual changes between the `evolog` commits by 353running `jj evolog -p`: 354 355```console 356$ # When was the last saved point before we started working on feature B? 357$ jj evolog -p --git # We use `--git` to make diffs clear without colors 358@ lnvvtrzo jjfan@example.org 2025-02-28 21:01:10 31a347e0 359│ featureA 360│ diff --git a/file b/file 361│ index 2b455c4207..2a7e05a01a 100644 362│ --- a/file 363│ +++ b/file 364│ @@ -1,1 +1,2 @@ 365│ Done with feature A 366│ +Working on feature B 367○ lnvvtrzo hidden jjfan@example.org 2025-02-28 21:00:51 b8004ab8 368│ featureA 369│ diff --git a/file b/file 370│ index cb61245109..2b455c4207 371│ --- a/file 372│ +++ b/file 373│ @@ -1,1 +1,1 @@ 374│ -Working on feature A 375│ +Done with feature A 376○ lnvvtrzo hidden jjfan@example.org 2025-02-28 20:50:05 e4d831d 377 (no description set) 378 diff --git a/file b/file 379 index 0000000000..cb61245109 380 --- /dev/null 381 +++ b/file 382 @@ 0,0 +1,1 @@ 383 +Working on feature A 384``` 385 386In this example, the version of the change when we were actually done with 387feature A is when we edited the file to say "Done with feature A". This state 388was saved in the commit with id `b80` (the second one in the list). The 389following edit (commit `31a`) belongs in a new change. 390 391#### Step 2: Create a new change for the current state and restore the existing change to the older state 392 393The "featureA" change is currently at commit `31a`: 394 395```console 396$ jj log 397@ lnvvtrzo jjfan@example.org 2025-02-28 21:01:10 31a347e0 398│ featureA 399◆ zzzzzzzz root() 00000000 400``` 401 402We'd like to create a new "featureB" change with the contents of the current 403commit `31a`, and we'd like the "featureA" change to be reverted to its former 404state at commit `b80` (see step 1 above for how we found that commit id). 405 406First, we create a new empty child commit. Since it is empty, it has the same 407contents as `31a`. 408 409```console 410$ jj new -m "featureB" 411Working copy (@) now at: pvnrkl 47171aa (empty) featureB 412Parent commit (@-) : lnvvtr 31a347e featureA 413$ cat file 414Done with feature A 415Working on feature B 416``` 417 418Now, we `jj restore` the change `lnvvtr` to its state at commit `b80`. We use 419the `--restore-descendants` flag so that the *file contents* (AKA snapshot) of 420the "featureB" change is preserved. 421 422```console 423$ # We refer to `lnvvtr` as `@-` for brevity 424$ jj restore --from b80 --into @- --restore-descendants 425Created lnvvtr 599994e featureA 426Rebased 1 descendant commits (while preserving their content) 427Working copy (@) now at: pvnrkl 468104c featureB 428Parent commit (@-) : lnvvtr 599994e featureA 429``` 430 431Even though `@-` was modified, `--restore-descendants` preserved the contents of 432the current change: 433 434```console 435$ jj file show -r @ file # Same as `cat file` 436Done with feature A 437Working on feature B 438$ jj file show -r @- file 439Done with feature A 440``` 441 442??? info "More details on what `--restore-descendants` does" 443 444 When we ran the `jj restore` command, the working copy change `@` was 445 at commit `471` and `@` was the only child of `@-`. In this situation, 446 447 ```shell 448 jj restore --from b80 --into @- --restore-descendants 449 ``` 450 451 is equivalent to 452 453 ```shell 454 jj restore --from b80 --into @- 455 jj restore --from 471 --into @ 456 ``` 457 458Now, we have achieved the exact state we desired: 459 460``` 461$ jj log -p --git 462@ pvnrklkn jjfan@example.org 2025-02-28 21:39:29 468104c2 463│ featureB 464│ diff --git a/file b/file 465│ index 2b455c4207..2a7e05a01a 100644 466│ --- a/file 467│ +++ b/file 468│ @@ -1,1 +1,2 @@ 469│ Done with feature A 470│ +Working on feature B 471○ lnvvtrzo jjfan@example.org 2025-02-28 21:39:29 599994ee 472│ featureA 473│ diff --git a/file b/file 474│ new file mode 100644 475│ index 0000000000..2b455c4207 476│ --- /dev/null 477│ +++ b/file 478│ @@ -0,0 +1,1 @@ 479│ +Done with feature A 480◆ zzzzzzzz root() 00000000 481$ jj diff --from b80 --to @- # No output means these are identical 482$ jj diff --from 31a --to @ # No output means these are identical 483``` 484 485 486### How do I resume working on an existing change? 487 488There are two ways to resume working on an earlier change: `jj new` then `jj squash`, 489and `jj edit`. The first is generally recommended, but `jj edit` can be useful. When 490you use `jj edit`, the revision is directly amended with your new changes, making it 491difficult to tell what exactly you change. You should avoid using `jj edit` when the 492revision has a conflict, as you may accidentally break the plain-text annotations on 493your state without realising. 494 495To start, use `jj new <rev>` to create a change based on that earlier revision. Make 496your edits, then use `jj squash` to update the earlier revision with those edits. 497For when you would use git stashing, use `jj edit <rev>` for expected behaviour. 498Other workflows may prefer `jj edit` as well. 499 500### Why are most merge commits marked as "(empty)"? 501 502Jujutsu, like Git, is a snapshot-based VCS. That means that each commit 503logically records the state of all current files in the repo. The changes in a 504commit are not recorded but are instead calculated when needed by comparing the 505commit's state to the parent commit's state. Jujutsu defines the changes in a 506commit to be relative to the auto-merged parents (if there's only one parent, 507then that merge is trivial - it's the parent commit's state). As a result, a 508merge commit that was a clean merge (no conflict resolution, no additional 509changes) is considered empty. Conversely, if the merge commit contains conflict 510resolutions or additional changes, then it will be considered non-empty. 511 512This definition of the changes in a commit is used throughout Jujutsu. It's 513used by `jj diff -r` and `jj log -p` to show the changes in a commit. It's used 514by `jj rebase` to rebase the changes in a commit. It's used in `jj log` to 515indicate which commits are empty. It's used in the `files()` revset function 516(and by `jj log <path>`) to find commits that modify a certain path. And so on. 517 518### How do I deal with divergent changes ('??' after the [change ID])? 519 520A [divergent change][glossary_divergent_change] represents a change that has two 521or more visible commits associated with it. To refer to such commits, you must 522use their [commit ID]. Most commonly, the way to resolve 523this is to abandon the unneeded commits (using `jj abandon <commit ID>`). If you 524would like to keep both commits with this change ID, you can `jj duplicate` one 525of them before abandoning it. 526 527### How do I deal with conflicted bookmarks ('??' after bookmark name)? 528 529A [conflicted bookmark][bookmarks_conflicts] is a bookmark that refers to multiple 530different commits because jj couldn't fully resolve its desired position. 531Resolving conflicted bookmarks is usually done by setting the bookmark to the 532correct commit using `jj bookmark move <name> --to <commit ID>`. 533 534Usually, the different commits associated with the conflicted bookmark should all 535appear in the log, but if they don't you can use `jj bookmark list`to show all the 536commits associated with it. 537 538### How do I integrate Jujutsu with Gerrit? 539 540At the moment you'll need a script, which adds the required fields for Gerrit 541like the `Change-Id` footer. Then `jj` can invoke it via an `$EDITOR` override 542in an aliased command. Here's an [example][gerrit-integration] from an 543contributor (look for the `jj signoff` alias). 544 545After you have attached the `Change-Id:` footer to the commit series, you'll 546have to manually invoke `git push` of `HEAD` on the underlying git repository 547into the remote Gerrit bookmark `refs/for/$BRANCH`, where `$BRANCH` is the base 548bookmark you want your changes to go to (e.g., `git push origin 549HEAD:refs/for/main`). Using a [co-located][co-located] repo 550will make the underlying git repo directly accessible from the working 551directory. 552 553We hope to integrate with Gerrit natively in the future. 554 555### I want to write a tool which integrates with Jujutsu. Should I use the library or parse the CLI? 556 557There are some trade-offs and there is no definitive answer yet. 558 559* Using `jj-lib` avoids parsing command output and makes error handling easier. 560* `jj-lib` is not a stable API, so you may have to make changes to your tool 561when the API changes. 562* The CLI is not stable either, so you may need to make your tool detect the 563different versions and call the right command. 564* Using the CLI means that your tool will work with custom-built `jj` binaries, 565like the one at Google (if you're using the library, you will not be able to 566detect custom backends and more). 567 568 569[bookmarks_conflicts]: bookmarks.md#conflicts 570 571[change]: glossary.md#change 572[change ID]: glossary.md#change-id 573[co-located]: glossary.md#co-located-repos 574[commit ID]: glossary.md#commit-id 575[commits]: glossary.md#commit 576[config]: config.md 577 578[gerrit-integration]: https://gist.github.com/thoughtpolice/8f2fd36ae17cd11b8e7bd93a70e31ad6 579[gitignore]: https://git-scm.com/docs/gitignore 580 581[glossary_divergent_change]: glossary.md#divergent-change 582 583[operator]: revsets.md#operators 584 585[revsets]: revsets.md 586 587[templates]: templates.md 588 589[reordering]: https://github.com/jj-vcs/jj/issues/1531