diff --git a/docs/FAQ.md b/docs/FAQ.md index 09a7c444c7..d0bc08b4fd 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -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 jjfan@example.org 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" + + +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 jjfan@example.org 2025-02-28 21:01:10 31a347e0 +│ featureA +○ lnvvtrzo hidden jjfan@example.org 2025-02-28 21:00:51 b8004ab8 +│ featureA +○ lnvvtrzo hidden jjfan@example.org 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 jjfan@example.org 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 jjfan@example.org 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 jjfan@example.org 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 jjfan@example.org 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 jjfan@example.org 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 jjfan@example.org 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? @@ -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