|
| 1 | +# Split a messy working copy |
| 2 | + |
| 3 | +````admonish reset title="Reset your progress" collapsible=true |
| 4 | +To reset your progress to the start of this chapter, run the following command: |
| 5 | +
|
| 6 | +```sh |
| 7 | +curl https://jj-for-everyone.github.io/reset.sh | bash -s commit_interactive |
| 8 | +cd ~/jj-tutorial/repo |
| 9 | +``` |
| 10 | +```` |
| 11 | + |
| 12 | +Recall the basic workflow we learned at the [very beginning](./commit.md): |
| 13 | + |
| 14 | +1. make some changes |
| 15 | +2. create a new commit |
| 16 | + |
| 17 | +...repeat. |
| 18 | +This is fine, but Alice is unfortunately a little forgetful. |
| 19 | +(Aren't we all?) |
| 20 | +Every once in a while, Alice makes a bunch of changes that are unrelated to each other, while forgetting to create a new commit in between. |
| 21 | +Let's see how that could look like. |
| 22 | + |
| 23 | +Assume Alice needs to complete three tasks. |
| 24 | +Task 1 is implemented in its own file, while tasks 2 and 3 are implemented in the same file: |
| 25 | +```sh |
| 26 | +echo "Implement task 1" > task_1.txt |
| 27 | +echo "Implement task 2" > tasks_2_and_3.txt |
| 28 | +echo "Implement task 3" >> tasks_2_and_3.txt |
| 29 | +``` |
| 30 | + |
| 31 | +<!-- generated by aha script --> |
| 32 | +<pre class="aha"> |
| 33 | +Commit ID: <span class="blue ">84ea100fcd1065a3edbdf1bc0dda2dc9446b147e</span> |
| 34 | +Change ID: <span class="purple ">pnwmqvrumpqkovqowswpxpuqnuospkxo</span> |
| 35 | +Author : <span class="yellow ">Alice</span> <<span class="yellow ">alice@local</span>> (<span class="cyan ">2025-11-06 15:37:35</span>) |
| 36 | +Committer: <span class="yellow ">Alice</span> <<span class="yellow ">alice@local</span>> (<span class="cyan ">2025-11-06 15:37:35</span>) |
| 37 | + |
| 38 | +<span class="yellow "> (no description set)</span> |
| 39 | + |
| 40 | +<span class="yellow ">Added regular file task_1.txt:</span> |
| 41 | + <span class="green "> 1</span>: <span class="underline "></span><span class="underline green ">Implement task 1</span><span class="green "></span> |
| 42 | +<span class="yellow ">Added regular file tasks_2_and_3.txt:</span> |
| 43 | + <span class="green "> 1</span>: <span class="underline "></span><span class="underline green ">Implement task 2</span><span class="green "></span> |
| 44 | + <span class="green "> 2</span>: <span class="underline "></span><span class="underline green ">Implement task 3</span><span class="green "></span> |
| 45 | +</pre> |
| 46 | + |
| 47 | +Alice could make a commit with the description "Implement tasks 1, 2 and 3". |
| 48 | +That would be fine in most situations. |
| 49 | +But sometimes, especially if these tasks are complex and require a lot of changes, it can be a mess. |
| 50 | +Putting them all in the same commit makes it harder to understand how the project evolved later on, both by Alice herself and other people like Bob. |
| 51 | +Lastly, if Alice makes a mistake while implementing, say, task 2, it is more difficult to revert the mistake without also losing the improvements made by tasks 1 and 3. |
| 52 | + |
| 53 | +If Alice wants to have those tasks in separate commits, how can she do that? |
| 54 | + |
| 55 | +## Committing only certain files |
| 56 | + |
| 57 | +The first option we'll look at is commit only certain files of the working copy. |
| 58 | +This works for task 1, which is isolated to its own file. |
| 59 | +To achieve this, you just pass the names of the files to commit as arguments to `jj commit`: |
| 60 | + |
| 61 | +```sh |
| 62 | +jj commit --message "Implement task 1" task_1.txt |
| 63 | +``` |
| 64 | + |
| 65 | +Let's inspect the commit produced by this command with `jj show @-`: |
| 66 | + |
| 67 | +<!-- generated by aha script --> |
| 68 | +<pre class="aha"> |
| 69 | +Commit ID: <span class="blue ">728297d2218d393b98f88638697f19c75a88a906</span> |
| 70 | +Change ID: <span class="purple ">pnwmqvrumpqkovqowswpxpuqnuospkxo</span> |
| 71 | +Author : <span class="yellow ">Alice</span> <<span class="yellow ">alice@local</span>> (<span class="cyan ">2025-11-06 15:37:35</span>) |
| 72 | +Committer: <span class="yellow ">Alice</span> <<span class="yellow ">alice@local</span>> (<span class="cyan ">2025-11-06 15:47:11</span>) |
| 73 | + |
| 74 | + Implement task 1 |
| 75 | + |
| 76 | +<span class="yellow ">Added regular file task_1.txt:</span> |
| 77 | + <span class="green "> 1</span>: <span class="underline "></span><span class="underline green ">Implement task 1</span><span class="green "></span> |
| 78 | +</pre> |
| 79 | + |
| 80 | +Splendid! |
| 81 | +And what does our current working copy commit look like? |
| 82 | +Let's check with `jj show`: |
| 83 | + |
| 84 | +<!-- generated by aha script --> |
| 85 | +<pre class="aha"> |
| 86 | +Commit ID: <span class="blue ">a0631dec487f5146078574ec6341ee64703839b8</span> |
| 87 | +Change ID: <span class="purple ">nqzqoxtywllzlltmzmyvovqrmolwotzy</span> |
| 88 | +Author : <span class="yellow ">Alice</span> <<span class="yellow ">alice@local</span>> (<span class="cyan ">2025-11-06 15:47:11</span>) |
| 89 | +Committer: <span class="yellow ">Alice</span> <<span class="yellow ">alice@local</span>> (<span class="cyan ">2025-11-06 15:47:11</span>) |
| 90 | + |
| 91 | +<span class="yellow "> (no description set)</span> |
| 92 | + |
| 93 | +<span class="yellow ">Added regular file tasks_2_and_3.txt:</span> |
| 94 | + <span class="green "> 1</span>: <span class="underline "></span><span class="underline green ">Implement task 2</span><span class="green "></span> |
| 95 | + <span class="green "> 2</span>: <span class="underline "></span><span class="underline green ">Implement task 3</span><span class="green "></span> |
| 96 | +</pre> |
| 97 | + |
| 98 | +As expected, the file for task 1 is not changed in our working copy anymore. |
| 99 | +The file for tasks 2 and 3 remain unchanged. |
| 100 | +In order to split a single file over multiple commits, we need something more powerful. |
| 101 | + |
| 102 | +## Committing only parts of a file |
| 103 | + |
| 104 | +The command `jj commit` has a flag `--interactive`. |
| 105 | +If you use it, Jujutsu will open a "terminal user interface" (TUI). |
| 106 | +It takes over your terminal to display a simple, text-based, graphical application. |
| 107 | +The way it works it not 100% intuitive for every user, so let's take it slow. |
| 108 | +Run the command: |
| 109 | + |
| 110 | +```sh |
| 111 | +jj commit --interactive |
| 112 | +``` |
| 113 | + |
| 114 | +You should see something like this: |
| 115 | + |
| 116 | + |
| 117 | + |
| 118 | +Let check out the menu bar first. |
| 119 | +Even though it's in the terminal, you can click on it! |
| 120 | +If you click on `[File]`, a drop-down menu with the options `[Confirm]` and `[Quit]` will open. |
| 121 | +It also shows the corresponding keyboard shortcuts: <kbd>c</kbd> and <kbd>q</kbd>. |
| 122 | +You can explore the other menus yourself, if you like. |
| 123 | + |
| 124 | +The changes in your working copy are below the menu bar. |
| 125 | +For now, we can only see the name of the file we changed. |
| 126 | +Hit the right arrow to "enter" the file, showing its content. |
| 127 | +It should look something like this: |
| 128 | + |
| 129 | + |
| 130 | + |
| 131 | +Indentation indicates a level of hierarchy: |
| 132 | +Files are at the top, next are "sections", i.e. consecutive changed lines, and lastly the individual lines. |
| 133 | + |
| 134 | +Now we can select the lines we want to commit first. |
| 135 | +In this case, it's the line of task 2. |
| 136 | +Navigate to that line with the arrow keys and select it with <kbd>Space</kbd>. |
| 137 | +It should look like this: |
| 138 | + |
| 139 | + |
| 140 | + |
| 141 | +Now that we have selected exactly the lines we want to end up in our commit, we can confirm by hitting <kbd>c</kbd> (or clicking on "File > Confirm" in the menu bar). |
| 142 | +The TUI will close, and your text editor will open for you to enter a commit message. |
| 143 | +Enter "Implement task 2", then save & quit. |
| 144 | +You can confirm that the file was correctly split over two commits with `jj show @-` and `jj show`. |
| 145 | + |
| 146 | +Let's tie up the remaining loose ends by committing task 3 and pushing the three new commits to the remote. |
| 147 | + |
| 148 | +```sh |
| 149 | +jj commit --message "Implement task 3" |
| 150 | +jj bookmark move main --to @- |
| 151 | +jj git push |
| 152 | +``` |
| 153 | + |
| 154 | +## |
| 155 | + |
| 156 | +```admonish success title="You've completed Level 3 ! 🎉" |
| 157 | +Now you have the skills to get yourself out of most problems that can come up when working with version control. |
| 158 | +Let's summarize what we've learned: |
| 159 | +- `jj undo` can restore previous states of your entire repository step-by-step. |
| 160 | + Don't worry about making mistakes! |
| 161 | + Experiment freely and `jj undo` if anything goes wrong. |
| 162 | +- If you want to work on a bookmark that only exists on the remote, for example after making a fresh clone of your repo, run `jj bookmark track`. |
| 163 | +- Combining two branches can lead to conflicts if they change the same part of a file. |
| 164 | + Resolve such conflicts by carefully removing the conflict markers to produce a sensible result. |
| 165 | +- If you end up with commits you no longer need for whatever reason, deleted them with `jj abandon`. |
| 166 | +- You can restore the content of a file using `jj restore`. |
| 167 | + By default, it restores to the parent of your working copy, but you can specify any commit to restore from with the `--from` flag. |
| 168 | +- If you end up with unrelated changes in your working copy, you can put them in separate commits with `jj commit --interactive`. |
| 169 | +
|
| 170 | +Now you know pretty much everything you need. |
| 171 | +You should be productive and rarely get stuck. |
| 172 | +Some readers will decide not to continue further with the tutorial, which is totally fine. |
| 173 | +
|
| 174 | +For those who want more, the next level will teach you how to rewrite history effortlessly. |
| 175 | +This skill will allow you to produce a much better commit history. |
| 176 | +Some projects like the Linux kernel or Jujutsu itself require their contributors to master these skills. |
| 177 | +Without them, contributors cannot meet the project's quality standards. |
| 178 | +
|
| 179 | +Aside from that, being able to rewrite history is simply freeing. |
| 180 | +You'll worry less about how to separate your changes into neat commits and what description to give them, because you can easily change these things later. |
| 181 | +
|
| 182 | +Nevertheless, feel free to take a long break before coming back here. |
| 183 | +You have learned a lot already, and it will be beneficial to have mastered that with practice before continuing. |
| 184 | +
|
| 185 | +At this point, you should also start exploring the Jujutsu CLI a little bit on your own. |
| 186 | +This tutorial is not a comprehensive reference of everything Jujutsu has to offer. |
| 187 | +Would you like to display all commits made by a specific author? |
| 188 | +Run `jj log --help` to find out how. |
| 189 | +Would you like to tweak the behavior of Jujutsu in some way? |
| 190 | +Maybe the [configuration guide](https://jj-vcs.github.io/jj/latest/config/) can help you out. |
| 191 | +``` |
0 commit comments