just playing with tangled
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