just playing with tangled
1# Using Jujutsu with GitHub and GitLab Projects
2
3This guide assumes a basic understanding of either Git or Mercurial.
4
5## Set up an SSH key
6
7As of October 2023 it's recommended to set up an SSH key to work with GitHub
8projects. See [GitHub's Tutorial][gh]. This restriction may be lifted in the
9future, see [issue #469][http-auth] for more information and progress on
10authenticated HTTP.
11
12## Basic workflow
13
14The simplest way to start with Jujutsu is to create a stack of commits first.
15You will only need to create a bookmark when you need to push the stack to a
16remote. There are two primary workflows: using a generated bookmark name or
17naming a bookmark.
18
19### Using a generated bookmark name
20
21In this example we're letting Jujutsu auto-create a bookmark.
22
23```shell
24# Start a new commit off of the default bookmark.
25$ jj new main
26# Refactor some files, then add a description and start a new commit
27$ jj commit -m 'refactor(foo): restructure foo()'
28# Add a feature, then add a description and start a new commit
29$ jj commit -m 'feat(bar): add support for bar'
30# Let Jujutsu generate a bookmark name and push that to GitHub. Note that we
31# push the working-copy commit's *parent* because the working-copy commit
32# itself is empty.
33$ jj git push -c @-
34```
35
36### Using a named bookmark
37
38In this example, we create a bookmark named `bar` and then push it to the remote.
39
40```shell
41# Start a new commit off of the default bookmark.
42$ jj new main
43# Refactor some files, then add a description and start a new commit
44$ jj commit -m 'refactor(foo): restructure foo()'
45# Add a feature, then add a description and start a new commit
46$ jj commit -m 'feat(bar): add support for bar'
47# Create a bookmark so we can push it to GitHub. Note that we created the bookmark
48# on the working-copy commit's *parent* because the working copy itself is empty.
49$ jj bookmark create bar -r @- # `bar` now contains the previous two commits.
50# Push the bookmark to GitHub (pushes only `bar`)
51$ jj git push --allow-new
52```
53
54While it's possible to create a bookmark in advance and commit on top of it in a
55Git-like manner, you will then need to move the bookmark manually when you create
56a new commits. Unlike Git, Jujutsu will not do it automatically.
57
58## Updating the repository
59
60As of October 2023, Jujutsu has no equivalent to a `git pull` command (see
61[issue #1039][sync-issue]). Until such a command is added, you need to use
62`jj git fetch` followed by a `jj rebase -d $main_bookmark` to update your
63changes.
64
65[sync-issue]: https://github.com/jj-vcs/jj/issues/1039
66
67## Working in a Git co-located repository
68
69After doing `jj git init --colocate`, Git will be in a [detached HEAD
70state][detached], which is unusual, as Git mainly works with named branches; jj
71does not.
72
73In a co-located repository, every `jj` command will automatically synchronize
74Jujutsu's view of the repo with Git's view. For example, `jj commit` updates the
75HEAD of the Git repository, enabling an incremental migration.
76
77```shell
78$ nvim docs/tutorial.md
79$ # Do some more work.
80$ jj commit -m "Update tutorial"
81# Create a bookmark on the working-copy commit's parent
82$ jj bookmark create doc-update -r @-
83$ jj git push --allow-new
84```
85
86## Working in a Jujutsu repository
87
88In a Jujutsu repository, the workflow is simplified. If there's no need for
89explicitly named bookmarks, you can just generate one for a change. As Jujutsu is
90able to create a bookmark for a revision.
91
92```shell
93$ # Do your work
94$ jj commit
95$ # Push change "mw", letting Jujutsu automatically create a bookmark called
96$ # "push-mwmpwkwknuz"
97$ jj git push --change mw
98```
99
100## Addressing review comments
101
102There are two workflows for addressing review comments, depending on your
103project's preference. Many projects prefer that you address comments by adding
104commits to your bookmark[^1]. Some projects (such as Jujutsu and LLVM) instead
105prefer that you keep your commits clean by rewriting them and then
106force-pushing[^2].
107
108### Adding new commits
109
110If your project prefers that you address review comments by adding commits on
111top, you can do that by doing something like this:
112
113```shell
114$ # Create a new commit on top of the `your-feature` bookmark from above.
115$ jj new your-feature
116$ # Address the comments by updating the code. Then review the changes.
117$ jj diff
118$ # Give the fix a description and create a new working-copy on top.
119$ jj commit -m 'address pr comments'
120$ # Update the bookmark to point to the new commit.
121$ jj bookmark move your-feature --to @-
122$ # Push it to your remote
123$ jj git push
124```
125
126Notably, the above workflow creates a new commit for you. The same can be
127achieved without creating a new commit.
128
129!!! warning
130
131 We strongly suggest to `jj new` after the example below, as all further edits
132 still get amended to the previous commit.
133
134```shell
135$ # Create a new commit on top of the `your-feature` bookmark from above.
136$ jj new your-feature
137$ # Address the comments by updating the code. Then review the changes.
138$ jj diff
139$ # Give the fix a description.
140$ jj describe -m 'address pr comments'
141$ # Update the bookmark to point to the current commit.
142$ jj bookmark move your-feature --to @
143$ # Push it to your remote
144$ jj git push
145```
146
147### Rewriting commits
148
149If your project prefers that you keep commits clean, you can do that by doing
150something like this:
151
152```shell
153$ # Create a new commit on top of the second-to-last commit in `your-feature`,
154$ # as reviewers requested a fix there.
155$ jj new your-feature- # NOTE: the trailing hyphen is not a typo!
156$ # Address the comments by updating the code. Then review the changes.
157$ jj diff
158$ # Squash the changes into the parent commit
159$ jj squash
160$ # Push the updated bookmark to the remote. Jujutsu automatically makes it a
161$ # force push
162$ jj git push --bookmark your-feature
163```
164
165The hyphen after `your-feature` comes from the
166[revset](https://github.com/jj-vcs/jj/blob/main/docs/revsets.md) syntax.
167
168## Working with other people's bookmarks
169
170By default, `jj git clone` imports the default remote bookmark (which is usually
171`main` or `master`), but `jj git fetch` doesn't import new remote bookmarks to
172local bookmarks. This means that if you want to iterate or test another
173contributor's bookmark, you'll need to do `jj new <bookmark>@<remote>` onto it.
174
175If you want to import all remote bookmarks including inactive ones, set
176`git.auto-local-bookmark = true` in the config file. Then you can specify a
177contributor's bookmark as `jj new <bookmark>` instead of `jj new <bookmark>@<remote>`.
178
179You can find more information on that setting [here][auto-bookmark].
180
181## Using GitHub CLI
182
183GitHub CLI will have trouble finding the proper Git repository path in jj repos
184that aren't [co-located](./git-compatibility.md#co-located-jujutsugit-repos)
185(see [issue #1008]). You can configure the `$GIT_DIR` environment variable to
186point it to the right path:
187
188```shell
189$ GIT_DIR=.jj/repo/store/git gh issue list
190```
191
192You can make that automatic by installing [direnv](https://direnv.net) and
193defining hooks in a `.envrc` file in the repository root to configure `$GIT_DIR`.
194Just add this line into `.envrc`:
195
196```shell
197export GIT_DIR=$PWD/.jj/repo/store/git
198```
199
200and run `direnv allow` to approve it for direnv to run. Then GitHub CLI will
201work automatically even in repos that aren't co-located so you can execute
202commands like `gh issue list` normally.
203
204[issue #1008]: https://github.com/jj-vcs/jj/issues/1008
205
206## Useful Revsets
207
208Log all revisions across all local bookmarks that aren't on the main bookmark nor
209on any remote:
210
211```shell
212$ jj log -r 'bookmarks() & ~(main | remote_bookmarks())'
213```
214
215Log all revisions that you authored, across all bookmarks that aren't on any
216remote:
217
218```shell
219$ jj log -r 'mine() & bookmarks() & ~remote_bookmarks()'
220```
221
222Log all remote bookmarks that you authored or committed to:
223
224```shell
225$ jj log -r 'remote_bookmarks() & (mine() | committer(your@email.com))'
226```
227
228Log all ancestors of the current working copy that aren't on any remote:
229
230```shell
231$ jj log -r 'remote_bookmarks()..@'
232```
233
234## Merge conflicts
235
236For a detailed overview, how Jujutsu handles conflicts, revisit
237the [tutorial][tut].
238
239[^1]:
240 This is a GitHub-style review, as GitHub currently only is able to compare
241 bookmarks.
242
243[^2]:
244 If you're wondering why we prefer clean commits in this project, see
245 e.g. [this blog post][stacked]
246
247[auto-bookmark]: config.md#automatic-local-bookmark-creation
248[detached]: https://git-scm.com/docs/git-checkout#_detached_head
249[gh]: https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent
250[http-auth]: https://github.com/jj-vcs/jj/issues/469
251[tut]: tutorial.md#conflicts
252[stacked]: https://jg.gg/2018/09/29/stacked-diffs-versus-pull-requests/
253
254## Using several remotes
255
256It is common to use several remotes when contributing to a shared repository.
257For example, "upstream" can designate the remote where the changes will be
258merged through a pull-request while "origin" is your private fork of the
259project.
260
261```shell
262$ jj git clone --remote upstream https://github.com/upstream-org/repo
263$ cd repo
264$ jj git remote add origin git@github.com:your-org/your-repo-fork
265```
266
267This will automatically setup your repository to track the main
268bookmark from the upstream repository, typically `main@upstream`
269or `master@upstream`.
270
271You might want to `jj git fetch` from "upstream" and to `jj git push`
272to "origin". You can configure the default remotes to fetch from and
273push to in your configuration file (for example,
274`.jj/repo/config.toml`):
275
276```toml
277[git]
278fetch = "upstream"
279push = "origin"
280```
281
282The default for both `git.fetch` and `git.push` is "origin".
283
284If you usually work on a project from several computers, you may
285configure `jj` to fetch from both repositories by default, in order to
286keep your own bookmarks synchronized through your `origin` repository:
287
288```toml
289[git]
290fetch = ["upstream", "origin"]
291push = "origin"
292```