Skip to content

Commit

Permalink
FAQ: detailed example for the "accidentally changed files in the wron…
Browse files Browse the repository at this point in the history
…g commit" question

It was requested on Discord once.

The result is almost a tutorial; we could consider actually moving it into the
tutorial and providing a link in the FAQ. That could be done separately.

The `jj` commands outputs are faked in a few, hopefully inconspicuous, ways.
Hopefully, this is not distracting.
  • Loading branch information
ilyagr committed Mar 4, 2025
1 parent 2af5b60 commit 4a664c7
Showing 1 changed file with 180 additions and 8 deletions.
188 changes: 180 additions & 8 deletions docs/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,14 +298,184 @@ jj config set --user git.private-commits "'''description(glob:'private:*')'''"

### I accidentally changed files in the wrong commit, how do I move the recent changes into another commit?

Use `jj evolog -p` to see how your working-copy commit has evolved. Find the
commit you want to restore the contents to. Let's say the current commit (with
the changes intended for a new commit) are in commit X and the state you wanted
is in commit Y. Note the commit id (normally in blue at the end of the line in
the log output) of each of them. Now use `jj new` to create a new working-copy
commit, then run `jj restore --from Y --into @-` to restore the parent commit
to the old state, and `jj restore --from X` to restore the new working-copy
commit to the new state.
Let's say we are editing a commit for "featureA", and we forgot to run `jj
new` or `jj commit` before doing some work that belongs in a new commit:

```console
$ jj log
@ lnvvtrzo [email protected] 2025-02-28 21:01:10 31a347e0
│ featureA
◆ zzzzzzzz root() 00000000
$ cat file # Oh no, the work on "feature B" should be in a separate commit!
Done with feature A
Working on feature B
```

#### Step 1: Find the commit id for the "last good version"

<!-- TODO: Reorganize the two related questions, this one and
-- the one linked below
-->
You can find [all the past versions of the working copy revision that `jj` has
saved](#jj-is-said-to-record-the-working-copy-after-jj-log-and-every-other-command-where-can-i-see-these-automatic-saves)
by running `jj evolog`. The obsolete versions will be marked as "hidden" and
will have the same change id, but will have different commit ids. This
represents different [commits] that are all parts of the same [change].

For example, this is what the evolog might look like after you made two edits to
the same change:

```console
$ # Note the word "hidden", the commit ids on the right,
$ # and the unchanging change id on the left.
$ jj evolog
@ lnvvtrzo [email protected] 2025-02-28 21:01:10 31a347e0
│ featureA
○ lnvvtrzo hidden [email protected] 2025-02-28 21:00:51 b8004ab8
│ featureA
○ lnvvtrzo hidden [email protected] 2025-02-28 20:50:05 e4d831d
(no description set)
```

Since commit `b800` is hidden, it is considered obsolete and `jj log` (without
arguments) will not show it, nor can it be accessed by its change id. However,
most `jj` operations work normally on such commits if you refer to them by their
commit id.

To find out which of these versions is the last time before we started working
on feature B (the point where we should have created a new change, but failed
to do so), we can look at the actual changes between the `evolog` commits by
running `jj evolog -p`:

```console
$ # When was the last saved point before we started working on feature B?
$ jj evolog -p --git # We use `--git` to make diffs clear without colors
@ lnvvtrzo [email protected] 2025-02-28 21:01:10 31a347e0
│ featureA
│ diff --git a/file b/file
│ index 2b455c4207..2a7e05a01a 100644
│ --- a/file
│ +++ b/file
│ @@ -1,1 +1,2 @@
│ Done with feature A
│ +Working on feature B
○ lnvvtrzo hidden [email protected] 2025-02-28 21:00:51 b8004ab8
│ featureA
│ diff --git a/file b/file
│ index cb61245109..2b455c4207
│ --- a/file
│ +++ b/file
│ @@ -1,1 +1,1 @@
│ -Working on feature A
│ +Done with feature A
○ lnvvtrzo hidden [email protected] 2025-02-28 20:50:05 e4d831d
(no description set)
diff --git a/file b/file
index 0000000000..cb61245109
--- /dev/null
+++ b/file
@@ 0,0 +1,1 @@
+Working on feature A
```

The version of the change when we were actually done with "featureA" thus
corresponds to the commit with id `b800`. The following edit (commit `31a3`)
belongs in a new change.

#### Step 2: Create a new change for the current state and restore the existing change to the older state

The "featureA" cahnge is currently at commit `31a`:

```console
$ jj log
@ lnvvtrzo [email protected] 2025-02-28 21:01:10 31a347e0
│ featureA
◆ zzzzzzzz root() 00000000
```

We'd like to create a new "featureB" change with the contents of the current
commit `31a`, and we'd the "featureA" change to be reverted to its former
state at commit `b80` (see step 1 above for how we found that commit id).

First, we create a new empty child commit. Since it is empty, it has the same
contents as `31a`.

```console
$ jj new -m "featureB"
Working copy now at: pvnrkl 47171aa (empty) featureB
Parent commit : lnvvtr 31a347e featureA
$ cat file
Done with feature A
Working on feature B
```

Now, we `jj restore` the change `lnvvtr` to its state at commit `b80`. We use
the `--restore-descendants` flag so that the *file contents* (AKA snapshot) of
the "featureB" change is preserved.

```console
$ # We refer to `lnvvtr` as `@-` for brevity
$ jj restore --from b80 --into @- --restore-descendants
Created lnvvtr 599994e featureA
Rebased 1 descendant commits (while preserving their content)
Working copy now at: pvnrkl 468104c featureB
Parent commit : lnvvtr 599994e featureA
```

Even though `@-` was modified, `--restore-descendants` preserved the contents of
the current change:

```console
$ jj file show -r @ file # Same as `cat file`
Done with feature A
Working on feature B
$ jj file show -r @- file
Done with feature A
```

??? info "More details on what `--restore-descendants` does"

When we ran the `jj restore` command, the working copy change `@` was
at commit `471` and `@` was the only child of `@-`. In this situation,

```shell
jj restore --from b80 --into @- --restore-descendants
```

is equivalent to

```shell
jj restore --from b80 --into @-
jj restore --from 471 --into @
```

Now, we have achieved the exact state we desired:

```
$ jj log -p --git
@ pvnrklkn [email protected] 2025-02-28 21:39:29 468104c2
│ featureB
│ diff --git a/file b/file
│ index 2b455c4207..2a7e05a01a 100644
│ --- a/file
│ +++ b/file
│ @@ -1,1 +1,2 @@
│ Done with feature A
│ +Working on feature B
○ lnvvtrzo [email protected] 2025-02-28 21:39:29 599994ee
│ featureA
│ diff --git a/file b/file
│ new file mode 100644
│ index 0000000000..2b455c4207
│ --- /dev/null
│ +++ b/file
│ @@ -0,0 +1,1 @@
│ +Done with feature A
◆ zzzzzzzz root() 00000000
$ jj diff --from b80 --to @- # No output means these are identical
$ jj diff --from 31a --to @ # No output means these are identical
```


### How do I resume working on an existing change?

Expand Down Expand Up @@ -392,9 +562,11 @@ detect custom backends and more).

[bookmarks_conflicts]: bookmarks.md#conflicts

[change]: glossary.md#change
[change ID]: glossary.md#change-id
[co-located]: glossary.md#co-located-repos
[commit ID]: glossary.md#commit-id
[commits]: glossary.md#commit
[config]: config.md

[gerrit-integration]: https://gist.github.com/thoughtpolice/8f2fd36ae17cd11b8e7bd93a70e31ad6
Expand Down

0 comments on commit 4a664c7

Please sign in to comment.