Commit graph

432 commits

Author SHA1 Message Date
Austin Seipp
37f11c7d4e workspace: add '--revision' argument to 'workspace add'
Summary: Workspaces are most useful to test different versions (commits) of
the tree within the same repository, but in many cases you want to check out a
specific commit within a workspace.

Make that trivial with a `--revision` option which will be used as the basis
for the new workspace. If no `-r` option is given, then the previous behavior
applies: the workspace is created with a working copy commit created on top of
the current working copy commit's parent.

Signed-off-by: Austin Seipp <aseipp@pobox.com>
Change-Id: I23549efe29bc23fb9f75437b6023c237
2023-10-11 22:02:24 -05:00
Martin von Zweigbergk
f654801c20 cli: accept TOML arrays and tables to jj config set 2023-10-11 07:40:08 -07:00
Yuya Nishihara
7a3e72415c cli: send status messages to stderr, specify stdout/stderr explicitly
Many of &mut UI can be changed to immutable borrows, but I'm not gonna
update them in this patch.
2023-10-11 19:24:01 +09:00
Martin von Zweigbergk
44eb902171 working_copy: don't crash when updating and tracked file exits on disk
Before this patch, when updating to a commit that has a file that's
currently an ignored file on disk, jj would crash. After this patch,
we instead leave the conflicting files or directories on disk. We
print a helpful message about how to inspect the differences between
the intended working copy and the actual working copy, and how to
discard the unintended changes.

Closes #976.
2023-10-07 14:02:31 -07:00
Yuya Nishihara
717d0d3d6d git: on deserialize/import/export, copy refs/heads/* to remote named "git"
I've added a boolean flag to the store to ensure that the migration never runs
more than once after the view gets "op restore"-d. I'll probably reorganize the
branches structure to support non-tracking branches later, but updating the
storage format in a single commit would be too involved.

If jj is downgraded, these "git" remote refs would be exported to the Git repo.
Users might have to remove them manually.
2023-10-07 19:33:35 +09:00
Martin von Zweigbergk
2ccb17b7b3 cli: enable tree-level conflicts by default
I have used the tree-level conflict format for several weeks without
problem (after the fix in 51b5d168ae). Now - right after the 0.10.0
release - seems like a good time to enable the config by default.

I enabled the config in our default configs in the CLI crate to reduce
impact on tests (compared to changing the default in `settings.rs`).
2023-10-05 10:31:47 -07:00
Martin von Zweigbergk
ce933507df release: release version 0.10.0
Thanks to everyone who's contributed!
2023-10-04 16:45:45 -07:00
Waleed Khan
d58664d40c command: add --interactive flag to jj commit 2023-09-30 22:03:51 -05:00
Waleed Khan
946a6e0bf7 commands: support path arguments to jj commit 2023-09-30 21:45:06 -05:00
Waleed Khan
d91dcc0c78 commands: add --interactive flag to jj split
`jj split` with no arguments operates interactively, but I am nonetheless constantly running `jj split -i` because I expect an `--interactive` flag to exist for consistency.

However, `jj split <paths>` before this commit always operates non-interactively, so this commit has the nice practical effect that you can restrict your interactive splitting to a certain set of paths.
2023-09-30 21:45:06 -05:00
Martin von Zweigbergk
a6ef3f0b6c cli: make set of immutable commits configurable
This adds a new `revset-aliases.immutable_heads()s` config for
defining the set of immutable commits. The set is defined as the
configured revset, as well as its ancestors, and the root commit
commit (even if the configured set is empty).

This patch also adds enforcement of the config where we already had
checks preventing rewrite of the root commit. The working-copy commit
is implicitly assumed to be writable in most cases. Specifically, we
won't prevent amending the working copy even if the user includes it
in the config but we do prevent `jj edit @` in that case. That seems
good enough to me. Maybe we should emit a warning when the working
copy is in the set of immutable commits.

Maybe we should add support for something more like [Mercurial's
phases](https://wiki.mercurial-scm.org/Phases), which is propagated on
push and pull. There's already some affordance for that in the view
object's `public_heads` field. However, this is simpler, especially
since we can't propagate the phase to Git remotes, and seems like a
good start. Also, it lets you say that commits authored by other users
are immutable, for example.

For now, the functionality is in the CLI library. I'm not sure if we
want to move it into the library crate. I'm leaning towards letting
library users do whatever they want without being restricted by
immutable commits. I do think we should move the functionality into a
future `ui-lib` or `ui-util` crate. That crate would have most of the
functionality in the current `cli_util` module (but in a
non-CLI-specific form).
2023-09-25 15:41:45 -07:00
Waleed Khan
0ed7c41200 changelog: fix broken link to revsets.md 2023-09-25 02:35:47 +02:00
Waleed Khan
409356fa5b merge_tools: enable :builtin as default diff/merge editor 2023-09-21 03:29:27 +02:00
Ruben Slabbert
f2f5ded5f0 revsets: add trunk alias with default to main/master/root 2023-09-17 10:17:23 +10:00
Zachary Dremann
2177dc0657 Allow \0 escape for nulls
This allows safely getting e.g. multiple descriptions, and knowing where the
boundaries are
2023-09-14 17:11:05 -04:00
Martin von Zweigbergk
8bfb6c10fd cli: add jj op log --no-graph
Seems useful, and makes it consistent with the `jj log` and `jj
obslog`.
2023-09-10 15:17:43 -07:00
Martin von Zweigbergk
70f6e0a452 cargo: enable the watchman feature by default
I think the feature is requested by enough users that we should
include it by default, also for people who install from source (we
include it in the `packaging` feature already).

It increases the size of the binary from 16.5 MiB to 17.8 MiB. I
suspect we'd see some of that increase in size soon anyway, as I'm
probably going to use Tokio for making async backend requests.
2023-09-08 09:34:55 -07:00
James Sully
0946934ca6 revset: Add optional argument n to ancestors() in revset language 2023-09-08 02:50:58 +10:00
Martin von Zweigbergk
bafd1b568e changelog: more orphan entries under headings 2023-09-06 11:32:48 -07:00
Martin von Zweigbergk
c7d1932546 release: release version 0.9.0
Thanks to everyone who's contributed!
2023-09-06 10:57:57 -07:00
Yuya Nishihara
a868b2d9a5 revset: do not lookup unimported tags or remote branches by unqualified name
Since e7e49527ef "git: ensure that remote branches never diverge", the last
known "refs/remotes" ref should be synced with the corresponding remote branch.
So we can always trust the branch@remote expression. We don't need "refs/tags"
lookup either since tags should have been imported by git::import_refs().

FWIW, I'm thinking of reorganizing view.git_refs() map as per-remote views.
It would be nice if we can get rid of revsets and template keywords exposing
low-level Git ref primitives.
2023-09-06 13:42:22 +09:00
Philip Metzger
f131dc9814 commands: Implement next and prev
This is a naive implementation, which cannot deal with multiple children
or parents stemming from merges.

Note: I gave each command separate a separate argument struct
for extensibility. 

Fixes #878
2023-09-05 23:13:39 +02:00
Yuya Nishihara
b0c8e9ef62 revset: add 0-ary "::" and ".." operators as short for "all()" and "~root()"
Suppose "x::y" is the operator that defaults to "root()::visible_heads()"
respectively, "::" is identical to "all()". Since we've just changed the
behavior of "..y", ".." is now "root()..visible_heads()" meaning "~root()".
2023-09-05 10:40:04 +09:00
Yuya Nishihara
6b2ad23f8f revset: evaluate "..y" expression to "root()..y"
This seems useful since the root commit is often uninteresting. It's also
consistent with "x::y" in a way that the left operand defaults to "root()".
2023-09-05 10:40:04 +09:00
Yuya Nishihara
e3c85d6ecc revset: convert root symbol to function
The idea is that we can fully eliminate special symbols that would otherwise
shadow user branches, tags, or change ID prefixes.

Closes #2095
2023-09-04 10:36:30 +09:00
Yuya Nishihara
35a596ff66 git: prohibit creation of remote named "git"
#1690
2023-08-29 22:50:46 +09:00
Yuya Nishihara
f422f1300c templater: move empty signature placeholder to user template
This patch also extracts format_detailed_signature() function to deduplicate
the "show" template bits.

The added placeholder templates aren't labeled as "empty". If needed, I think
the whole template can be labeled as "empty" (or "empty_commit") just like
"working_copy".

Closes #2112
2023-08-27 09:38:36 +09:00
Yuya Nishihara
75ebdf69a1 revset: parse @ like operator (but without alias substitution)
This is what I proposed in #2095. @ is now an operator to concatenate symbols.

Unlike the other operators, lhs/rhs of @ is not a target of alias substitution.
'x' in 'x@y' doesn't look like a named variable, though it's technically
possible to allow definition of an alias expanded to a symbol of specific remote
or vice versa. This will probably apply to the kind:pattern syntax, where
aliases are expanded due to the current implementation restriction. I've added
a TODO comment about that.
2023-08-26 07:47:12 +09:00
Zachary Dremann
ac448202da templates: Add more string methods
Add starts_with/ends_with/remove_prefix/remove_suffix/substr methods to string when templating.
2023-08-24 11:24:07 -04:00
Vladimir Petrzhikovskii
386f002f5a git: add --all-remotes to git fetch 2023-08-24 12:52:41 +02:00
Martin von Zweigbergk
0dcd2fa265 cli: make jj git push default to -r 'remote_branches()..@'
The way `jj git push` without arguments chooses branches pointing to
either `@` or `@-` is unusual and difficult to explain. Now that we
have `-r`, we could instead default it to `-r '@-::@'`. However, I
think it seems likely that users will want to push all local branches
leading up to `@` from the closest remote branch. That's typically
what I want. This patch changes the default to do that.
2023-08-23 15:00:03 -07:00
Piotr Kufel
2109a7b488 Fix .gitignore handling of ignored directories
- Ignore .gitignore files from untracked directories
 - Do not allow un-ignoring files within ignored directories
2023-08-22 22:08:32 -07:00
Ilya Grigoriev
038867fd3f merge_tools: Allow 3-pane diff editing
As discussed in https://github.com/martinvonz/jj/discussions/1905#discussioncomment-6589673
2023-08-21 20:19:15 -07:00
Martin von Zweigbergk
5f3df4aaea revset: resolve "@" symbol's workspace id earlier (while parsing)
We resolve file paths into repo-relative paths while parsing the
revset expression, so I think it's consistent to also resolve which
workspace "@" refers to while parsing it. That means we won't need the
workspace context both while parsing and while resolving symbols.

In order to break things like `author("martinvonz@")` (thanks to @yuja
for catching this), I also changed the parsing of working-copy
expressions so they are not allowed to be
quoted. `author(martinvonz@)` will therefore be an error now. That
seems like a small improvement anyway, since we have recently talked
about making `root` and `[workspace]@` not parsed as other symbols.
2023-08-20 17:57:18 -07:00
Preston Van Loon
ac5d8eb784 Add UTC format for timestamp formats. Thanks to @rauljordan for these changes.
Add tests for new UTC timestamp format

Add documentation for timestamp utc

Update CHANGELOG.md
2023-08-20 17:24:09 -05:00
Oscar Bonilla
5bd726f77d Add jj diffs --stat option 2023-08-19 23:49:16 -07:00
Yuya Nishihara
b6794ca04a revset: rename literal:"" prefix to exact:""
Per discussion in #2107, I believe "exact" is preferred.

We can also change the default to exact match, but it doesn't always make
sense. Exact match would be useful for branches(), but not for description().
We could define default per predicate function, but I'm pretty sure I cannot
remember which one is which.
2023-08-19 11:33:57 +09:00
Emily Fox
3f8ac2198d commits: use empty strings instead of placeholders for missing name or email
This commit replaces the functions `UserSettings::user_name_placeholder()`` and
`UserSettings::user_email_placeholder()` with `const` `&str`s to emphasize that
the placeholder strings must not be changed to support commits without
names or email addresses made before this change.
2023-08-18 17:22:59 -05:00
Emily Fox
fde6e43aa8 CHANGELOG: more precise description of mine() 2023-08-18 17:13:45 -05:00
Ben Saunders
351e7feef5 working_copy: don't snapshot new files larger than 1MiB by default 2023-08-17 19:29:38 -07:00
Yuya Nishihara
81f1ae38b3 revset: add literal:"string" pattern syntax
The syntax is slightly different from Mercurial. In Mercurial, a pattern must
be quoted like "<kind>:<needle>". In JJ, <kind> is a separate parsing node, and
it must not appear in a quoted string. This allows us to report unknown prefix
as an error.

There's another subtle behavior difference. In Mercurial, branch(unknown) is
an error, whereas our branches(literal:unknown) is resolved to an empty set.
I think erroring out doesn't make sense for JJ since branches() by default
performs substring matching, so its behavior is more like a filter.

The parser abuses DAG range syntax for now. It can be rewritten once we remove
the deprecated x:y range syntax.
2023-08-17 07:42:12 +09:00
Emily Fox
9ba9ecd708 revset: add function mine() 2023-08-16 11:00:14 -05:00
Martin von Zweigbergk
873634a80e cli: when splitting change without description, skip it on second part
One use case for `jj split` is when creating a new commit from some of
the changes in the working copy. If there's no description on the
working-copy commit in that case, it seems better to not ask the user
to provide one when they're splitting the commit either.
2023-08-15 10:12:12 -07:00
Anton Bulakh
dc6e1d7dee cli: hide irrelevant information about root commit in default log templates
I've extracted the `builtin_log_root` template for users to customize the
default templates without fully overriding them, for example I would remove
the change_id/commit_id for myself - and we discussed in Discord that leaving
those makes sense for the user to be reminded/teached that the root commit has
a change id made from z's.
2023-08-15 18:54:59 +03:00
Anton Bulakh
82923afcc5 templater: add root keyword
Similar to other boolean flags, such as "working_copy" or "empty".
We could test something like
`"0000000000000000000000000000000000000000".contains(commit_id)`
like I did for myself, but first of all this is ugly, and secondly the root
commit id is not guaranteed to be 40 zeroes as custom backend implementations
could have some other root.
2023-08-15 18:54:59 +03:00
Piotr Kufel
a8b02de5c3 Allow editing user-specific config file outside of a repo
This addresses #2054.
2023-08-13 11:35:56 -07:00
Vamsi Avula
bc57754c58 cli: add support for setting default description
That is, jj will use ui.default_description as a starting point when
user is about to describe an empty change.

I think it might be confusing to do this with -m / --stdin (violates
WYSIWYG), so I'm only doing this when jj invokes an editor.

Also, this could evolve into a proper template in the future instead of
just plain text, to allow inheriting from parent change(s), for example.

Partially addresses #1354.
2023-08-13 23:59:15 +05:30
Zachary Dremann
062f7a252b cli: Allow repeated -m options for multi-paragraph descriptions
Emulates git's behavior:
https://www.git-scm.com/docs/git-commit#Documentation/git-commit.txt--mltmsggt
2023-08-13 05:06:35 -04:00
Matt Stavola
4760b565c5 configs: add the ability to disable paging via ui.paginate 2023-08-11 12:45:53 -04:00
Yuya Nishihara
17b45d642f cli: add "--limit N" option to log-like commands
Copied from Mercurial. This isn't a revset predicate since our revset is
conceptually unordered.
2023-08-11 10:40:40 +09:00
Martin von Zweigbergk
a367f13c72 configs: use Notepad as default editor on Windows 2023-08-10 17:09:07 +00:00
Yuya Nishihara
b2101d15c8 cli: detect .git symlink as a colocated workspace
Maybe we could load GitBackend without resolving .git symlink, but that would
introduce more subtle bugs. Instead, we calculate the expected Git workdir path
from the canonical ".git" path.

Fixes #2011
2023-08-10 14:53:54 +09:00
Martin von Zweigbergk
56472297f0 git: add support for SSH authentication with ed25519 or ed25519-sk
This makes it possible to use ed25519 and ed25519-sk keys by trying
them one at a time. However, it still fails if one of them is
password-protected; we don't try the next key in that case.
2023-08-09 03:44:03 +00:00
Martin von Zweigbergk
c752b43db1 git: only try to use ssh-agent once per connection
As reported in #1970, SSH authentication would sometimes run into a
loop where it repeatedly tries to use ssh-agent for authentication
without making progess. The problem can be reproduced by simply
removing `$SSH_AUTH_KEY` from your environment (and not having a Git
credentials helper configured, I think).

This seems to be a bug introduced by b104f8e154c21. That commit meant
to make it so we attempt to use ssh-agent and fall back to using
(password-less) keys after that. The problem is that
`git2::Cred::ssh_key_from_agent()` just returns an object that will be
used later for looking up the credentials from ssh-agent, so the call
will not fail because ssh-agent is not reachable.

This commit attempts to fix the problem by having the credentials
callback attempt to use ssh-agent only once.
2023-08-08 07:41:13 +00:00
Ilya Grigoriev
74d9970908 config: Rename push.branch-prefix option to git.push-branch-prefix
This is for consistency with other `git.` options. See also
https://github.com/martinvonz/jj/pull/1962#discussion_r1282605185
2023-08-07 19:10:10 -07:00
Piotr Kufel
10cdef49f2 Allow initializing with bare git repositories 2023-08-03 23:54:27 -07:00
Yuya Nishihara
0b9d23c3ad cli: add option to generate textual diff by external command
This is basic implementation. There's no config knob to enable the external
diff command by default. It reuses the merge-tools table because that's how
external diff/merge commands are currently configured. We might want to
reorganize them in #1285.

If you run "jj diff --tool meld", GUI diff will open and jj will wait for
meld to quit. This also applies to "jj log -p". The "diff --tool gui" behavior
is somewhat useful, but "log -p --tool gui" wouldn't. We might want some flag
to mark the tool output can't be streamed.

Another thing to consider is tools that can't generate directory diffs. Git
executes ext-diff tool per file, but we don't. Difftastic can compare
directories, and doing that should be more efficient since diffs can be
computed in parallel (at the expense of unsorted output.)

Closes #1886
2023-08-03 13:53:37 +09:00
Ilya Grigoriev
315f698231 cli: New -changes-in argument to jj restore + docs
Also updates docstrings for `diffedit`, `abandon` in related ways. The changes
are a bit too intertwined to comfortable split into a separate commit.
2023-08-01 15:28:00 -07:00
Chris Poucet
56e6233f9e commands: Support the option of colocating a git repo with a jj repo
This adds the new --colocate flag to `jj git clone`.

```
jj git clone --colocate https://github.com/foo/bar
```

is effectively equivalent to:

```
git clone https://github.com/foo/bar
cd bar
jj init --git-repo=.
```
2023-07-29 18:43:17 +02:00
Martin von Zweigbergk
9c8d8b73b8 cli: replace --allow-large-revsets by use of all: prefix
See the earlier commit introducing the `::` operator for reasoning.
2023-07-28 22:30:40 -07:00
Martin von Zweigbergk
aaf5df95a8 rebase/new: make --allow-large-revset no longer also allow duplicates
The `--allow-large-revset` option for `jj rebase` and `jj new` is used
for allowing a single revset to resolve to more than one destination
commit. It also means that duplicate commits between individual
revsets are allowed (e.g. `jj rebase -d x -d 'x|y'`). I'm about to
replace the first meaning of the flag by a revset function. I don't
think it's worth keeping the flag only for the second meaning, so I'm
just removing the feature instead. We can add it back under a
different name (`--allow-duplicate-destinations`?) if people care
about it.
2023-07-28 22:30:40 -07:00
Martin von Zweigbergk
c183b94aef cli: warn when using : revset operator 2023-07-28 22:30:40 -07:00
Martin von Zweigbergk
48580ed8b1 revsets: allow :: as synonym for :
The `--allow-large-revsets` flag we have on `jj rebase` and `jj new`
allows the user to do e.g. `jj rebase --allow-large-revsets -b
main.. -d main` to rebase all commits that are not in main onto
main. The reason we don't allow these revsets to resolve to multiple
commits by default is that we think users might specify multiple
commits by mistake. That's probably not much of a problem with `jj
rebase -b` (maybe we should always allow that to resolve to multiple
commits), but the user might want to know if `jj rebase -d @-`
resolves to multiple commits.

One problem with having a flag to allow multiple commits is that it
needs to be added to every command where we want to allow multiple
commits but default to one. Also, it should probably apply to each
revset argument those commands take. For example, even if the user
meant `-b main..` to resolve to multiple commits, they might not have
meant `-d main` to resolve to multiple commits (which it will in case
of a conflicted branch), so we might want separate
`--allow-large-revsets-in-destination` and
`--allow-large-revsets-in-source`, which gets quite cumbersome. It
seems better to have some syntax in the individual revsets for saying
that multiple commits are allowed.

One proposal I had was to use a `multiple()` revset function which
would have no effect in general but would be used as a marker if used
at the top level (e.g. `jj rebase -d 'multiple(@-)'`). After some
discussion on the PR adding that function (#1911), it seems that the
consensus is to instead use a prefix like `many:` or `all:`. That
avoids the problem with having a function that has no effect unless
it's used at the top level (`jj rebase -d 'multiple(x)|y'` would have
no effect).

Since we already have the `:` operator for DAG ranges, we need to
change it to make room for `many:`/`all:` syntax. This commit starts
that by allowing both `:` and `::`.

I have tried to update the documentation in this commit to either
mention both forms, or just the new and preferred `::` form. However,
it's useless to search for `:` in Rust code, so I'm sure I've missed
many instances. We'll have to address those as we notice them. I'll
let most tests use `:` until we deprecate it or delete it.
2023-07-28 22:30:40 -07:00
Piotr Kufel
04d120aad0 Allow editing non-existent configs
Addresses #1571.
2023-07-27 15:49:08 -07:00
Yuya Nishihara
4834d12c37 simple_op_store: serialize RefTarget in new format (breaks downgrades)
This is breaking change. Old jj binary will panic if it sees a view saved by
new jj. Alternatively, we can store both new and legacy data for backward
compatibility.
2023-07-27 15:32:48 +09:00
Yuya Nishihara
fb33620f9e revset_graph: group commits topologically
The original idea was similar to Mercurial's "topo" sorting, but it was bad
at handling merge-heavy history. In order to render merges of topic branches
nicely, we need to prioritize branches at merge point, not at fork point.
OTOH, we do also want to place unmerged branches as close to the fork point
as possible. This commit implements the former requirement, and the latter
will be addressed by the next commit.

I think this is similar to Git's sorting logic described in the following blog
post. In our case, the in-degree walk can be dumb since topological order is
guaranteed by the index. We keep HashSet<CommitId> instead of an in-degree
integer value, which will be used in the next commit to resolve new heads as
late as possible.

https://github.blog/2022-08-30-gits-database-internals-ii-commit-history-queries/#topological-sorting

Compared to Sapling's beautify_graph(), this is lazy, and can roughly preserve
the index (or chronological) order. I tried beautify_graph() with prioritizing
the @ commit, but the result seemed too aggressively reordered. Perhaps, for
more complex history, beautify_graph() would produce a better result. For my
wip branches (~30 branches, a couple of commits per branch), this works pretty
well.

#242
2023-07-25 01:45:37 +09:00
Austin Seipp
8cb429d065 chore(rust): bump MSRV to 1.71.0
Summary: Let's be more aggressive about tracking the latest stable Rust release.
There's little benefit to being conservative so early on, especially when no
users seem to have faced any issue with upgrading, or strictly required an old
Rust version.

Right now, just lagging Rust by 1 major release probably seems fine. We're
targeting 1.71.0 to get ahead of the curve, since 1.72.0 will likely release
sometime before the next `jj` release.

Signed-off-by: Austin Seipp <aseipp@pobox.com>
Change-Id: I4e691b6ba63b5b9023a75ae0a6917672
2023-07-17 18:38:26 -05:00
Martin von Zweigbergk
af7e044c4c changelog: add @mlcui-google as contributor to 0.8.0
@mlcui-google made their first contribution after I drafted the
release notes for 0.8.0 and I forgot to update the release notes
before merging the PR.
2023-07-17 11:23:44 +05:30
Martin von Zweigbergk
8149ec6df6 release: release version 0.8.0
Thanks to everyone who's contributed!
2023-07-16 21:40:24 +01:00
Martin von Zweigbergk
aac5b7aa25 cargo: rename crates from jujutsu/jujutsu-lib to jj-cli/jj-lib
Almost everyone calls the project "jj", and there seeems to be
consensus that we should rename the crates. I originally wanted the
crates to be called `jj` and `jj-lib`, but `jj` was already
taken. `jj-cli` is probably at least as good for it anyway.

Once we've published a 0.8.0 under the new names, we'll release 0.7.1
versions under the old names with pointers to the new crates names.
2023-07-09 06:40:43 +02:00
Yuya Nishihara
d58702cd63 templater: include pseudo @git targets in branches keyword
#1666
2023-07-09 10:39:43 +09:00
Yuya Nishihara
a934547720 git: consistently ignore unrelated refs on fetch()
Since "jj git fetch --branch '*'" doesn't import unrelated remote and local
refs, "jj git fetch" shouldn't do either.
2023-07-09 10:08:46 +09:00
Waleed Khan
f15c1d3c53 docs(fsmonitor): add filesystem monitor/Watchman documentation 2023-07-08 18:48:14 +03:00
Ilya Grigoriev
8645153946 undo: preserve git-tracking refs in colocated repos by default 2023-07-03 12:28:06 -07:00
Ilya Grigoriev
371e7f46e2 git fetch: do a git export of deleted branches before fetch 2023-07-03 11:01:22 -07:00
Yuya Nishihara
cefbeba776 cli: add revset filter to "branch list"
Typical query would be something like -r 'mine()' or -r 'branches()' to
exclude remote-only branches #1136.

The query matches against local targets only. This means there's no way to
select deleted/forgotten branches by -r option. If we add a default revset
configuration, we'll need some way to turn the default off.
2023-07-02 14:39:45 +09:00
Grégoire Geis
fee7eb5813 add --edit option to jj sparse set 2023-06-27 22:56:46 +09:00
Yuya Nishihara
453d3a11fc changelog: mention fixes for #924 and #1608 (racy change id / snapshot) 2023-06-26 14:24:37 +09:00
Ilya Grigoriev
bdb6db88e1 cli: new jj chmod command to set executable bit 2023-06-24 13:28:01 -07:00
Ilya Grigoriev
e41d672cc0 branch forget: allow forgetting deleted branches
Partially fixes #1537
2023-06-24 09:23:14 -07:00
Waleed Khan
24ea8478cb feat(config): add jj config get for scripting
The motivating use-case was this `jj signoff` script: https://gist.github.com/thoughtpolice/8f2fd36ae17cd11b8e7bd93a70e31ad6

Which includes lines like this:

```sh
NAME=$(jj config list user.name | awk '{split($0, a, "="); print a[2];}' | tr -d '"')
MAIL=$(jj config list user.email | awk '{split($0, a, "="); print a[2];}' | tr -d '"')
```

There is no reason that we should have to clumsily parse out the config values. This `jj config get` command supports scripting use-cases like this.
2023-06-23 12:07:39 -07:00
Ilya Grigoriev
096538ba18 revsets: stop jj parsing br as a git_ref refs/heads/br
Use `br@git` instead.

Before, if there is not a local branch `br`, jj tried to resolve
it as a git ref `refs/heads/br`. Unchanged from before, `br` can
still be resolved as a tag `refs/tag/br`.
2023-06-12 14:31:44 -07:00
Ilya Grigoriev
a483252cf2 revset: allow checking out git-tracking (@git) branches 2023-06-12 14:31:44 -07:00
Ilya Grigoriev
8df945b71d cmd: have jj branch list report git-tracking (@git) branches
This doesn't change the way @git branches are stored in `git_refs` as opposed
to inside `BranchTarget` like normal remote-tracking branches. There are
subtle differences in behavior with e.g. `jj branch forget` and I'm not sure
how easy it is to rewrite `jj git import/export` to support a different
way of storage.

I've decided to call these "local-git tracking branches" since they track
branches in the local git repository. "local git-tracking" branches sounds a
bit more natural, but these could be confused with there are no remote
git-tracking branches. If one had the idea these might exist, they would be
confused with remote-tracking branches in the local git repo.

This addresses a portion of #1666
2023-06-12 14:31:44 -07:00
Waleed Khan
74b846870c feat(diff): add ui.diff-instructions option to suppress JJ-INSTRUCTIONS file 2023-06-06 22:43:14 -07:00
Martin von Zweigbergk
7f3d07e35f cli: add jj git push -r for pushing branches pointing to revset
I think I will find this useful in at least two cases:

1. When you already have a branch pointing to some commit, it's easier
   to do `jj git push -r xyz` than `jj git push --branch
   push-xyzxyzyxzxyz`.

2. When you have a stack of changes, it's useful to be able to push
   all of them at once.

I think we should also update the default behavior of `jj git push` to
be `jj git push -r 'remote_branches()..@'` or something like
that. That removes the ugliness of having a default behavior that the
user can't reproduce using flags. I'll leave that change for a
separate PR.
2023-06-04 20:50:11 -07:00
Martin von Zweigbergk
abcfebf1e2 cli: allow jj git push --change xyz --branch foo
This was pretty simple. I simplified a bit by making the transaction
description mention only branches, not changes. It still mentions the
branches created for the changes, however. Also, since the operation
"tags" contain the full command line, I think it'll still be
relatively easy for the user to understand what the operation was
about.
2023-06-04 20:50:11 -07:00
Martin von Zweigbergk
f4499aa65e conflicts: fix bug when modifying modify/delete conflicts
Currently, if the user modifies a modify/delete conflict, we always
consider the result resolved. That happens because we materialize the
missing side of the conflict as an empty string but when we parse the
conflict, we expect only the number of sides in the input
conflict. For example, if the input is a regular modify/delete
conflict with one remove and one add, the materialized markers will
have one remove and two adds (one of them empty), but when we try to
parse it, we expect one remove and only one add. When we fail to parse
it, we consider it resolved.

This commit fixes the bug by using
`conflicts::Conflict<Option<TreeValue>>` and keeping track of which
sides were supposed to be empty. We could have fixed the bug without
switching to `conflicts::Conflict`, but we want to switch anyway, and
the fix happens naturally when switching.
2023-06-04 06:48:34 -07:00
Martin von Zweigbergk
864c17b8c4 changelog: consider renamed jj debug commands a breaking change
I think I originally didn't consider changing debug commands as a
breaking change, but it's better to be conservative.
2023-06-03 19:21:43 -07:00
Martin von Zweigbergk
e86f339863 changelog: removing the jj hide alias is a breaking change 2023-06-03 19:21:43 -07:00
Martin von Zweigbergk
b6691a1d23 cli: make jj sparse --list a subcommand
`jj sparse` is a bit different from other commands in that its `jj
sparse --list` is practically a separate command. Let's make it an
actual subcommand for consistency, and so we can more cleanly add
additional flags for `jj sparse list` in the future. I moved all the
other arguments to `jj sparse set`. I'm not sure if `jj sparse set
--reset` would have been better as `jj sparse reset`, but it is
technically just updating the sparse patterns just like the other
arguments (`--clear`, `--add` , `--remove`).
2023-05-22 09:49:38 -07:00
Ilya Grigoriev
714aff63e6 git.rs: properly abandon commits from moved/deleted branches on remote (#864)
This bug concerns the way `import_refs` that gets called by `fetch` computes
the heads that should be visible after the import.

Previously, the list of such heads was computed *before* local branches were
updated based on changes to the remote branches. So, commits that should have
been abandoned based on this update of the local branches weren't properly
abandoned.

Now, `import_refs` tracks the heads that need to be visible because of some ref
in a mapping keyed by the ref. If the ref moves or is deleted, the
corresponding heads are updated.

Fixes #864
2023-05-17 17:57:58 -07:00
Martin von Zweigbergk
eab5218fe5 cli: allow resolving shorter ids within a configured revset
This adds a config called `revsets.short-prefixes`, which lets the
user specify a revset in which to disambiguate otherwise ambiguous
change/commit ids. It defaults to the value of `revsets.log`.


I made it so you can disable the feature by setting
`revsets.short-prefixes = ""`. I don't like that the default value
(using `revsets.log`) cannot be configured explicitly by the
user. That will be addressed if we decide to merge the `[revsets]` and
`[revset-aliases]` sections some day.
2023-05-11 23:41:24 -07:00
Martin von Zweigbergk
ac31c83e13 cli: rename ui.default-revset to revsets.log
I plan to add `revsets.short-prefixes` and `revsets.immutable` soon,
and I think `[revsets]` seems like reasonable place to put them. It
seems consistent with our `[templates]` section. However, it also
suffers from the same problem as that section, which is that the
difference between `[templates]` and `[template-aliases]` is not
clear. We can decide about about templates and revsets later.
2023-05-11 23:41:24 -07:00
Yuya Nishihara
92cfffd843 git: on external HEAD move, do not abandon old branch
The current behavior was introduced by 20eb9ecec1 "git: don't abandon
HEAD commit when it loses a branch." While the change made HEAD mutation
behavior more consistent with a plain ref operation, HEAD can also move on
checkout, and checkout shouldn't be considered a history rewriting operation.

I'm not saying the new behavior is always correct, but I think it's safer
than losing old HEAD branch. I also think this change will help if we want
to extract HEAD management function from git::import_refs().

Fixes #1042.
2023-05-11 10:15:31 +09:00
Aaron Bull Schaefer
6445ccea7b cli: use a unique file extension for description tempfiles
Establishing a unique file extension for the temporary files created
via `jj describe` helps to ensure that text editors can recognize the
filetype and alter settings accordingly. This will open the door for
an improved user experience, and allow for setting things like the
appropriate text-width/rulers, syntax highlighting of the diff summary
(see Git's commit tree-sitter grammer [1]), easy toggling of the `JJ:`
comment lines, etc.

I examined the behavior of filetype detection across a number of
common text editors, and the most universally-support mechanism was
to have a unique extension that does not include any periods. Meaning
that namespacing via something like `.jj.txt` instead, won't always be
detected due to inconsistent matching prioritization across editors.
It also makes sense to assume that we may want other Jujutsu-specific
filetypes in the future.

The filename prefix has also been switched to be `editor-` for clarity,
as well as to ease matching a glob-pattern if we ever need to garbage
collect leftover tempfiles. This structure is similar to what Mercurial
and Sapling do as well.

[1] https://github.com/the-mikedavis/tree-sitter-git-commit
2023-04-18 18:35:19 -07:00
Aaron Bull Schaefer
6c627fb30d cli: default to log when no subcommand is provided
This is a convenience optimization to improve the default user
experience, since `jj log` is a frequently run command. Accessing the
help information explicitly still follows normal CLI conventions, and
instructions are displayed appropriately if the user happens to make a
mistake. Discoverability should not be adversely harmed.

Note that this behavior mirrors what Sapling does [2], where `sl` will
display the smartlog by default.

[1] https://github.com/clap-rs/clap/issues/975
[2] https://sapling-scm.com/docs/overview/smartlog
2023-04-17 16:30:42 -07:00
Anton Bulakh
791b821115 cmd: when a single remote is defined, default to it for git fetch/push
A simple quick implementation of what I've suggested on discord
2023-04-14 08:20:30 +03:00
Martin von Zweigbergk
92a911b7a3 cli: rename jj support to jj util
I wasn't quite happy with `jj support` but I couldn't think of
anything better when I moved the commands from `jj debug` in
e2b4d7058d. Thanks to @ilyagr for suggesting `jj util`.
2023-04-12 21:34:47 -07:00
Ilya Grigoriev
d50c0f5085 Templater: label hidden commits (aka abandoned commits)
Looks like "change_id normal" + color. Picture for `jj obslog`:

https://user-images.githubusercontent.com/4123047/230708271-4108cf5f-255d-419e-bd3e-fc97dc8b8660.png

This should also be occasionally be useful for e.g. `jj log commit_id`.

I also considered the wording `(Was change_id)` or `change_id (old)`, `change_id (obs)`.
2023-04-09 22:56:33 -07:00
Ilya Grigoriev
c735d92e8c formatter: allow using "default" terminal color
The "default" color resets the terminal color to default.
2023-04-09 22:56:33 -07:00
Martin von Zweigbergk
24a512683b revset: add a revset function for finding commits with conflicts
This adds `conflict()` revset that selects commits with conflicts. We
may want to extend it later to consider only conflicts at certain
paths.
2023-04-06 16:46:21 -07:00
Martin von Zweigbergk
ac27cdea69 cli: include total downloaded size in progress output
With this change, the output looks like this:

```
  3% 165.9 Mi at  13.4 MiB/s [█▋                                                   ]
```

Closes #1483.
2023-04-05 06:21:32 -07:00
Ilya Grigoriev
233dda1057 cmd: prohibit creating branches at the root commit
Such branches lead to confusing errors on git expoert or push.
2023-04-03 23:51:47 -07:00
Ilya Grigoriev
31f7c806e2 squash/amend: Add -m argument to set description
This prevents an editor opening, and is useful in scripts/tests.
2023-04-03 23:51:15 -07:00
Martin von Zweigbergk
e1c57338a1 revset: split out no-args head() to visible_heads()
The `heads()` revset function with one argument is the counterpart to
`roots()`. Without arguments, it returns the visible heads in the
repo, i.e. `heads(all())`. The two use cases are quite different, and
I think it would be good to clarify that the no-arg form returns the
visible heads, so let's split that out to a new `visible_heads()`
function.
2023-04-03 23:46:34 -07:00
Ilya Grigoriev
0e2579ee6a Switch graph node to use for commit instead of
This follows up on 5c703aeb03.

The only reason for this change is that, subjectively, the result looks better to me. I'm not sure why, but I couldn't get used to the old symbol in spite of its seeming reasonableness. It felt really bold and heavy.

If people agree, we can wait until we need to update the screenshots for some other reason before merging this. Sorry I didn't figure this out while the discussion about the referenced commit was going on.

I'm not 100% certain how many fonts support each symbol. Please try it out and let me know if it doesn't work for you.

Compare after:

![image](https://user-images.githubusercontent.com/4123047/229251383-563b889d-7233-42e2-a3c5-bf9368a4d1fd.png)

and before:

![image](https://user-images.githubusercontent.com/4123047/229251695-7fd0ff2c-2832-4262-ade5-5120288cccdf.png)
2023-04-02 23:15:37 -07:00
Yuya Nishihara
0532301e03 revset: add latest(candidates, count) predicate
This serves the role of limit() in Mercurial. Since revsets in JJ is
(conceptually) an unordered set, a "limit" predicate should define its
ordering criteria. That's why the added predicate is named as "latest".

Closes #1110
2023-03-25 23:48:50 +09:00
Martin von Zweigbergk
fc84c02c8e cli: add jj describe --no-edit to not open editor 2023-03-19 00:48:05 -07:00
Martin von Zweigbergk
3bacc367cd cli: add jj describe --reset-author
I think requests to reset the author came up twice in the last week,
so let's just add support for it. I copied git's behavior of resetting
the name, email, and timestamp. The flag name is also from git.
2023-03-19 00:48:05 -07:00
Martin von Zweigbergk
2495c8f27e cargo: update MSRV to 1.64
We need 1.64 to bump `clap` to `4.1`. We don't really need to upgrade
to that, but being on an older version causes minor confusions like
#1393. Rust 1.64 is very close to 6 months old at this point.
2023-03-17 22:44:29 -07:00
Martin von Zweigbergk
e2b4d7058d cli: move some debug commands to new (non-hidden) support group
The `jj debug` commands are hidden from help and are described as
"Low-level commands not intended for users", but e.g. `jj debug
completion` is intended for users, and should be visible in the help
output.
2023-03-17 06:50:55 -07:00
Martin von Zweigbergk
ce098a729a cli: remove jj hide as alias for jj abandon
I haven't heard of anyone using this alias, so let's remove it. It's
easy for users to add it as their own alias if they really want it.
2023-03-17 06:50:55 -07:00
Martin von Zweigbergk
e64ca31bfd cli: show diff summary as two states instead of transition
By using one letter for the path type before and one letter for path
type after, we can encode much more information than just the current
'M'/'A'/'R'. In particular, we can indicate new and resolved
conflicts. The color still encodes the same information as before. The
output looks a bit weird after many years of using `hg status`. It's a
bit more similar to the `git status -s` format with one letter for the
index and one with the working copy. Will we get used to it and find
it useful?
2023-03-16 08:01:13 -07:00
Martin von Zweigbergk
5c703aeb03 cli: replace o as graph node by when using unicode graph
@joyously found `o` confusing because it's a valid change id prefix. I
don't have much preference, but `●` seems fine. The "ascii",
"ascii-large", and "legacy" graph styles still use "o".

I didn't change `@` since it seems useful to have that match the
symbol used on the CLI. I don't think we want to have users do
something like `jj co ◎-`.
2023-03-12 23:21:05 -07:00
Yuya Nishihara
904e9c5520 cli: add ui.log-word-wrap option
Unlike Mercurial, this isn't a template keyword/function, but a config knob.
Exposing graph_width to templater wouldn't be easy, and I don't think it's
better to handle terminal wrapping in template.

I'm not sure if patch content should be wrapped, so this option only applies
to the template output for now.

Closes #1043
2023-03-11 12:01:17 +09:00
Samuel Tardieu
decca920c7 git push: do not consider @- if @ has non-empty content or description 2023-03-05 23:50:20 +01:00
Yuya Nishihara
66458a097e templater: require infix ++ operator to concatenate expressions
This eliminates ambiguous parsing between "func()" and "expr ()".

I chose "++" as template concatenation operator in case we want to add
bit-wise negate operator. It's also easier to find/replace than "~".
2023-03-01 16:39:23 +09:00
David Barnett
99cb0ba7c5 Implement "config set" subcommand
Uses toml_edit to support simple config edits like:
  jj config set --repo user.email "somebody@example.com"
2023-02-28 18:18:02 -08:00
Yuya Nishihara
bab13e1982 cli: snapshot stale working copy before updating
Since there's no easy API to snapshot the stale working copy without releasing
the lock, we have to compare the tree ids after reacquiring the lock. We could
instead manually snapshot and rebase the working-copy commit, but that would
require more copy-paste codes.

Closes #1310
2023-02-28 12:59:30 +09:00
Samuel Tardieu
c5e41a99c3 git remote rename: rename git refs as well 2023-02-26 15:22:18 +01:00
Samuel Tardieu
b515d14f18 git_fetch: when removing a remote branch, remove git ref as well 2023-02-26 15:22:18 +01:00
Ilya Grigoriev
30d03a66e6 cmd: --branch option for git fetch.
Thanks to @samueltardieu for noticing a subtle bug in the refspecs, providing
the fix, as well as the two `conflicting_branches` tests.
2023-02-21 18:33:40 -08:00
Yuya Nishihara
b5f1728ffb templater: migrate op log to template language
The outermost "op-log" label isn't moved to the default template. I think
it belongs to the command's formatter rather than the template.

Old bikeshedding items:
- "current_head", "is_head", or "is_head_op"
  => renamed to "current_operation"
- "templates.op-log" vs "templates.op_log" (the whole template is labeled
  as "op-log")
  => renamed to "op_log"
- "template-aliases.'format_operation_duration(time_range)'"
  => renamed to 'format_time_range(time_range)'
2023-02-20 18:20:41 +09:00
Ilya Grigoriev
9c51d74b2c cmd: Allow multiple -b for jj rebase
This also makes `rebase_branch` reuse `rebase_descendants`.

This addresses a portion of #1158
2023-02-20 00:36:32 -08:00
Ilya Grigoriev
f04458a245 cmd: Allow multiple -s for jj rebase
This addresses a portion of #1158

One application (not the prettiest, but useful until we have `jj sync`):

    jj rebase -s oldmain+~:main -d main -L
2023-02-20 00:36:32 -08:00
Ilya Grigoriev
53476a77f7 cmd: Make jj restore work if @ is a merge commit
To be clear, this applies to `jj restore` without any arguments.

Fixes #1228
2023-02-19 22:55:53 -08:00
Samuel Tardieu
863a6e6d09 git push: add --deleted option 2023-02-19 18:18:53 +01:00
Martin von Zweigbergk
f70e6987b5 conflicts: preserve order of adds in materialized conflict
We write conflict to the working copy by materializing them as
conflict markers in a file. When the file has been modified (or just
the mtime has changed), we parse the markers to reconstruct the
conflict. For example, let's say we see this conflict marker:

```
<<<<<<<
+++++++
b
%%%%%%%
-a
+c
>>>>>>>
```

Then we will create a hunk with ["a"] as removed and ["b", "c"] as
added.

Now, since commit b84be06c08, when we materialize conflicts, we
minimize the diff part of the marker (the `%%%%%%%` part). The problem
is that that minimization may result in a different order of the
positive conflict terms. That's particularly bad because we do the
minimization per hunk, so we can end up reconstructing an input that
never existed.

This commit fixes the bug by only considering the next add and the one
after that, and emitting either only the first with `%%%%%%%`, or both
of them, with the first one in `++++++++` and the second one in
`%%%%%%%`.

Note that the recent fix to add context to modify/delete conflicts
means that when we parse modified such conflicts, we'll always
consider them resolved, since the expected adds/removes we pass will
not match what's actually in the file. That doesn't seem so bad, and
it's not obvious what the fix should be, so I'll leave that for later.
2023-02-18 22:01:25 -08:00
Martin von Zweigbergk
e48ace56d1 conflicts: replace missing files by empty in materialized conflict
When we materialize modify/delete conflicts, we currently don't
include any context lines. That's because modify/delete conflicts have
only two sides, so there's no common base to compare to. Hunks that
are unchanged on the "modify" side are therefore not considered
conflicting, and since they they don't contribute new changes, they're
simply skipped (here:
3dfedf5814/lib/src/files.rs (L228-L230)).

It seems more useful to instead pretend that the missing side is an
empty file. That way we'll get a conflict in the entire file.

We can still decide later to make e.g. `jj resolve` prompt the user on
modify/delete conflicts just like `hg resolve` does (or maybe it
actually happens earlier there, I don't remember).

Closes #1244.
2023-02-17 22:19:04 -08:00
Martin von Zweigbergk
eb79a21cc0 release: release version 0.7.0
It's been about 10 weeks and 730 commits since 0.6.0, compared to
about 7 weeks and 350 commits between 0.5.0 and 0.6.0, so it's time
for a new release. There's been significant user-visible changes and
code-quality improvements. Thanks, everyone!
2023-02-16 12:50:52 -08:00
Ilya Grigoriev
e4aa2cb2e5 Rename ui.relative-timestamps to ui.oplog-relative-timestamps 2023-02-15 21:26:14 -08:00
Ilya Grigoriev
859b0f680c Make ui.relative-timestamps default to true
This seems like a better default for `jj op log`, which is now the only thing
this option affects.
2023-02-15 21:26:14 -08:00
Yuya Nishihara
3c61e9239c config: remove ui.log-author-format in favor of template alias 2023-02-16 11:43:17 +09:00
Yuya Nishihara
6316ec7442 config: migrate log/show timestamp format away from config knob
Since oplog still relies on ui.relative-timestamps, this config key
isn't removed.
2023-02-16 11:43:17 +09:00
Yuya Nishihara
a00767bc0f config: remove ui.unique-prefixes/log-id-preferred-length in favor of alias 2023-02-16 11:43:17 +09:00
Yuya Nishihara
7913f90869 templater: remove "brackets" short id style in favor of template alias
Though .prefix() + .rest() has to call .shortest() twice, I don't think
the added cost would be significant.
2023-02-16 11:43:17 +09:00
Yuya Nishihara
605eb72883 config: rename [template] section to [templates], drop .graph suffix
This should be more consistent with the other tables.
2023-02-15 00:23:48 +09:00
Martin von Zweigbergk
331ebf4cef cli: use reverse-alphabet hex for branch name in jj git push --change 2023-02-13 22:49:21 -08:00
Martin von Zweigbergk
9887d2c3d6 templater: use reverse-alphabet hex for change ids 2023-02-13 22:49:21 -08:00
Yuya Nishihara
bfdaaa4257 templater: implement symbol/function alias expansion
Test vectors are mainly copied from revset.rs and adapted to the template
syntax.

Closes #1190
2023-02-14 10:06:28 +09:00
Samuel Tardieu
c0c3f87574 git fetch: prune old branch names before adding new ones 2023-02-12 02:10:17 +01:00
Vamsi Avula
98261e81e2 Fix typos and slightly simplify code from #1235 2023-02-12 03:17:40 +05:30
Vamsi Avula
daf7b656e3 config: add and parse ui.log_author_format for use in the default template
Supported values are,

- `none` for no author information,
- `full` for both the name and email,
- `name` for just the name,
- `username` for username part of the email,
- (default) `email` (or any other gibberish for that matter) for the full email.
2023-02-11 20:54:23 +05:30
Vamsi Avula
aacdcf629b author/committer templates: add a username method
I haven't used a proper email address parser as I'm not really sure if it's
worth the extra dependency and effort -- thoughts?
2023-02-11 15:38:27 +05:30
Martin von Zweigbergk
ee336a2e45 cli: delete builtin update/up aliases
These don't seem used much, and maybe we (or a customized build) will
want to use the name in the future.
2023-02-10 08:33:05 -08:00
Yuya Nishihara
a1bfe33a08 cli: expand $left/$right parameters in merge-tools.<name>.edit-args
And set edit_args = ["$left", "$right"] by default.
2023-02-07 18:32:57 +09:00
Samuel Tardieu
605a39b84f jj new --insert-after 2023-02-07 09:16:48 +01:00
Samuel Tardieu
4119aa44a9 jj new --insert-before 2023-02-07 09:16:48 +01:00
Martin von Zweigbergk
8d3d89facc cli: rename diff.format to ui.diff.format
I felt that the config is too narrow to have it's own top-level [diff]
section, and I couldn't think of another good place to have it. I'm
happy to hear other suggestions.
2023-02-05 23:36:30 -08:00