After I changed `merge-tools.vimdiff.program` to `vim`, using
`vimdiff` as a diff editor wouldn't work at all.
Out of the box, it's still not a good experience. I included a
recommendation of a plugin to install to make it better.
The implementation has some hoops to jump through because Rust does not allow
`self: &Arc<Self>` on trait methods, and two of the OpHeadsStore functions need
to return cloned selves. This is worked around by making the implementation type
itself a wrapper around Arc<>.
This is not particularly note worthy for the current implementation type where
the only data copied is a PathBuf, but for extensions it is likely to be more
critical that the lifetime management of the OpHeadsStore is properly
maintained.
I ran an upgraded Clippy on the codebase. All the changes seem to be
about using variables directly in format strings instead of passing
them as separate arguments.
"jj log -p --summary" now shows summary and color-words diff, like
"hg log -p --stat".
Handling of "-p" is tricky. I first considered "-p" would turn on the default
diff output, but I found it would be confusing if "jj log -p --git" showed
both color-words and git diffs. So the default format is inserted only if
no --git nor --color-words is explicitly specified.
This allows us to generate diff in different formats. There are various ways
to achieve that:
a. build TreeDiffIterator for each format (this patch)
b. make TreeDiffIterator clonable
c. collect TreeDiffIterator and reuse the resulting vec
(a) and (b) are practically the same. (c) would be more efficient if building
and iterating TreeDiffIterator were expensive, but I don't think it is because
Tree is cached by Store. If we add $GIT_EXTERNAL_DIFF-like feature, we'll
probably need Tree objects to snapshot them to /tmp. So I chose (a).
The author timestamp is rarely useful (in my experience). The
committer timestamp, on the other hand, can be useful for
understanding when a change was most recently modified. IIRC, I
originally picked the author timestamp to match the email (which is
the author's), but it's probably not confusing to use the author email
and the committer timestamp. I suspect few users will even reflect on
it.
Clap bails parsing when an "error" is encountered, e.g. a subcommand is missing,
"--help" is passed, or the "help" subcommand is invoked. This means that the
current approach of parsing args does not handle flags like `--no-pager` or
`--color` when an error is encountered.
Fix this by separating early args into their own struct and preprocessing them
using `ignore_errors` (per https://github.com/clap-rs/clap/issues/1880).
The early args are in a new `EarlyArgs` struct because of a known bug where
`ignore_errors` causes default values not to be respected
(https://github.com/clap-rs/clap/issues/4391 specifically calls out bool, but
strings may also be ignored), so when `ignore_errors` is given, the default
values will be missing and parsing will fail unless the right arg types are used
(e.g`. Option`). By parsing only early args (using the new struct) we only need
to adjust `no_pager`, instead of adjusting all args with a default value.
The number of lines in the diff output is unchanged.
This makes diffs a little more readable when the "..." would otherwise hide a
single line of code that helps in understanding the surrounding context lines.
This change mostly rearranges the loop that consumes the diff lines, so it can
buffer up to num_context_lines*2+1 lines instead of just num_context_lines.
There's a bit of extra code to handle times when a "..." replaces the last line
of a diff.
Note that `jj diff --git` is unchanged, and will still output `@@` lines that
replace a single line of context.
This fixes the bug described in the previous commit.
Because we now print the message about failed exports also while
snapshotting, we may end up reporting it twice on one command. I'm not
sure it's worth worrying about that. We can deal with that later if it
turns out to be a common complaint.
Unfortunately, config::Value is lax and '[7]' could be parsed as '["7"]'.
I don't like it, but I think that's actually better for consistency as we
use config.get_string() in various places.
When a workspace's working-copy commit is updated from another
workspace, the workspace becomes "stale". That means that the working
copy on disk doesn't represent the commit that the repo's view says it
should. In this state, we currently automatically it to the desired
commit next time the user runs any command in the workspace. That can
be undesirable e.g. if the user had a slow build or test run started
in the working copy. It can also be surprising that a checkout happens
when the user ran a seemingly readonly command like `jj status`.
This patch makes most commands instead error out if the working copy
is stale, and adds a `jj workspace update-stale` to update it. The
user can still run commands with `--no-commit-working-copy` in this
state (doing e.g. `jj --no-commit-working-copy rebase -r @ -d @--` is
another way of getting into the stale-working-copy state, by the way).
I want to be able to create a `WorkspaceCommandHelper` without
snapshotting the working copy. That will be useful when adding a
command for updating a stale working copy.
This patch makes the function checking for a stale working copy return
a specialized error instead of `CommandError`. That has several advantages:
* It makes it easier to change the behavior so we don't automatically
update a stale working copy until the user explicitly tells us.
* It allows us to move the function to the library crate, to be
shared by non-CLI UIs (and server applications).
Similar to the previous commit, the checking for stale working copy is
a big part of `commit_working_copy()` and it's logically quite
separated from the rest, so let's extract it to a function.
The code for creating a `WorkspaceCommandHelper` for a given
`Workspace` is a well-separated part of the large
`workspace_helper()`, so lets extract it to a function.
Also allows several paths to be specified. By default, `jj resolve`
will find the first conflict that matches provided paths (if any)
and try to resolve it.
It seems like I forgot to update the `jj status` output when I decided
(years ago?) that the changes in a commit should always be compared to
the auto-merged parents. I was very confused before I realized that
`jj status` was showing the diff summary against the first parent. I
suppose the fact that `jj status` lists only one parent should have
been a hint. Thanks to ilyagr@ for finding this odd behavior. This
patch fixes it by making the command list all parents, and changes the
diff summary to be against the auto-merged parents.
The `print` command shows the contents of a file, so that is obviously
often more than a page long. Both `hg cat file` and `git show
HEAD:file` page the output.
The output from `files` is often longer than a screen, so the pager is
useful, even though this command is probably used mostly by
scripts. As with `status`, `hg` pages its output, but `git` doesn't.
The status output may be long, so the pager can useful. Now that we
pass `-F` to the pager by default, it should also be fine to use the
pager for short output. For reference, `hg` pages `status` output, but
`git` doesn't.
As dbarnett@ reported on #9, our default of `less`, combined with our
default of enabling color on TTYs, means that we print ANSI codes to
`less` by default. Unless the user has set e.g. `$LESS=R`, `less` is
going to escape those codes, resulting in garbage like this:
```
@ ESC[1;35mbb39c26a29feESC[0m ESC[1;33m(no email configured)ESC[0m ESC[1;36m2022-12-03....
```
I guess most of us didn't notice because we have something like
`$LESS=FRX` set.
This patch changes our default from `less` to `less -FRX`. Those are
the flags we're using for our internal hg distribution at Google, and
that has seemed quite uncontroversial.
I added a pointer from the changelog to the tracking issue while at
it.
If this new option is not specified, we start with empty output
file and trust the merge tool did a complete merge no matter
what the file contains.
Includes tests.
This command uses an external merge tool to resolve conflicts
simple enough that they can be resolved with a 3-way merge.
This commit provides a very basic version of `jj resolve` that
is hardcoded to use vimdiff.
This also slightly changes the errors of the Diff Editor, so that
both the diff editor and `jj resolve can share an error type.
It should be more reliable than parsing a command string into array.
Also updated some of the doc example to use array syntax. I don't think
"C:/Program Files" was parsed properly, but might work thanks to Windows
magic.
It implements Deserialize because config.get() requires that. We could instead
add TryFrom<config::Value>, but we'll need Deserialize anyway if we want to
parse a struct containing FullCommandArgs.
I don't know if src/config.rs is the right place, but I feel it's slightly
better than messing up ui.rs.
The example for the `-b` flag was completely incorrect. It looks like
I have copied the example from `-r` and then forgotten to update
it. This fixes that, and also adds some more commits to the example to
hopefully clarify.
Teach Ui's writing functions to write to a pager without touching the
process's file descriptors. This is done by introducing UiOutput::Paged,
which spawns a pager that Ui's functions can write to.
The pager program can be chosen via `ui.pager`. (defaults to Defaults to
$PAGER, and 'less' if that is unset (falling back to 'less' also makes
the tests pass).
Currently, commands are paginated if:
- they have "long" output (as defined by jj developers)
- jj is invoked in a terminal
The next commit will allow pagination to be turned off via a CLI option.
More complex pagination toggling (e.g. showing a pager even if the
output doesn't look like a terminal, using a pager for shorter ouput) is
left for a future PR.
We'll add a variant that isn't a pair. Also add a function to create a
new UiOutput::Terminal, we will create this variant in a few places
because we want to fall back to it.
I can't see any reason the user would want to specify revisions
matching the empty string, so let's disallow it. I created a custom
type for revision arguments instead of repeating `value_parser =
NonEmptyStringValueParser::new()`.
If the user creates a branch with an empty name, it seems very likely
to be an accident. Let's help them realize that by erroring out.
I didn't add the same checks to `jj branch delete`, since that would
make it hard to delete a branch with an empty name from existing
repos.
Let's acknowledge everyone's contributions by replacing "Google LLC"
in the copyright header by "The Jujutsu Authors". If I understand
correctly, it won't have any legal effect, but maybe it still helps
reduce concerns from contributors (though I haven't heard any
concerns).
Google employees can read about Google's policy at
go/releasing/contributions#copyright.
Aliases are loaded at WorkspaceCommandHelper::new() as it's easier to warn
invalid declarations there. Not all commands use revsets, but many do, so
I think it's okay to always pay the loading cost. Parsing the declaration
part (i.e. a symbol) should be fast anyway.
The nested error message isn't super readable, but seems good enough.
Config syntax to bikeshed:
- naming: [revset-alias] vs [revset-aliases] ?
- function alias will need quotes: 'f(x)' = 'x'
The CLI will load aliases from config, insert them one by one, and warn if
declaration part is invalid. That's why RevsetAliasesMap is a public struct
and needs to be instantiated by the caller.
This adds a warning whenever export to the backing Git repo fails,
whether it's by an explicit `jj git export` or an automatic export. It
might be too spammy to print the message after every failed command in
the colocated case, but let's try it and see.
This is a simple workaround to make operation IDs predictable (so they
don't include the path to `.../target/debug/jj` or similar). The full
path can probably be useful for troubleshooting, but we can deal with
that later if we see a need for it. It might also be less confusing
for Windows users if it says "jj.exe".
It would be nice to be able to use snapshot testing and not have to
parse the output of `jj op log`. This patch lets us do that by
providing a new environment variable and config for overriding the
timestamps. Unlike `operation.hostname` and `operation.username`,
these are only meant for tests.
This makes the tests more hermetic, even though I don't think the
default values (taken from `whoami`) can break any tests (then we
would have already seen them break). Now we just need to make the
operation log's timestamps predictable and then we can start using
operation IDs in snapshot tests.
The `atty` crate seems unmaintained. There's
https://rustsec.org/advisories/RUSTSEC-2021-0145 filed against it,
which `cargo-deny` complains about. A fix for that has been open for
well over a year without being fixed
(https://github.com/softprops/atty/pull/51). It turns out the
functionality is also available via the `crossterm` crate (thanks,
@yuja), which we already depend on.
Since we also depend on `atty` via `clap`, I also added an exception
to the `cargo-deny` config.
There are no "non-normal" files, so "normal" is not needed. We have
symlinks and conflicts, but they are not files, so I think just "file"
is unambiguous.
I left `testutils::write_normal_file()` because there it's used to
mean "not executable file" (there's also a `write_executable_file()`).
I left `working_copy::FileType::Normal` since renaming `Normal` there
to `File` would also suggest we should rename `FileType`, and I don't
know what would be a better name for that type.
We currently get the hostname and username from the `whoami` crate. We
do that in lib crate, without giving the caller a way to override
them. That seems wrong since it might be used in a server and
performing operations on behalf of some other user. This commit makes
the hostname and username configurable, so the calling crate can pass
them in. If they have not been passed in, we still default to the
values from the `whoami` crate.
We have talked about showing the commit ID only for divergent changes
because it's generally easier to work with the change ID, and it's
less likely to result in a divergent change. However, it's useful to
have the commit ID available for pasting into e.g. a commit message or
the GitHub UI. To try to steer users towards using the change ID, this
commit moves the commit ID off to the right in the log output.
I put it just after the "divergent" field, because that makes it close
to how I imagine it would look if we decided to hide the commit ID
except for divergent changes. I was thinking that could be rendered as
"divergent (abc123)". So if we add config to hide the commit ID, then
it would be rendered almost the same for divergent commits (just with
the added parentheses). It would also make sense to replace the
"divergent" field by a question mark on the change ID, since change
IDs basically behave like branches. If we do that, then the placement
of the commit ID I picked in this commit does not make sense.
Given how easy this was, I can't believe I didn't make the change
sooner.
I haven't updated the screenshots in the readme because I plan to make
some further changes to the default template. I'll update them after
those changes.
I'm going to add other error variants for when we fail to read/write
to disk, and I don't think we need to have a custom message for each
in the CLI crate. It's easier to pass along the message from the lib
crate.
(`ConflictedBranch`, on the other hand, is an expected error so I
think we should continue to let let the CLI crate define the error
message for it.)
Several lines of red text can be overwhelming, and makes it harder to
tell the hint from the error. Let's separate the hint from the error
instead. This matches what hg does. Having the hints separated out
also means that we could have a single config to turn them off.
I want to add a separate field for a hint, so that can be printed in a
different color (than red). Having a factory function is useful then,
since most call sites don't want to pass a hint. Also, by using a
factory function instead of using the constructor directly means that
we can accept `&str` arguments instead of forcing the caller to
convert a string literal to `String`.
It's useful to know when you've modified a branch that exists on a
remote. A typical case is when you have pushed a branch to a remote
and then rewritten it. This commit adds an indication in the
`branches` template keyword. A branch that needs to be pushed to a
remote now has a `*` at the end (similar to how conflicted branches
have a `?` at the end). Note that the indication only considers
remotes where the branch currently exists, so there won't be an
indication that the branch has not been pushed to a remote.
Closes#254
Example:
JJ: Enter commit description for the first part (parent).
Better split commit message
JJ: This part contains the following changes:
JJ: M src/formatter.rs
JJ:
JJ: Lines starting with "JJ: " (like this one) will be removed.
Unfortunately, TOML requires quotes around the argument. So, the
usage is `jj --config-toml ui.color=\"always\"` in bash. The plan is
to eventually have a `--config` option with simpler syntax for
simple cases.
As discussed in https://github.com/martinvonz/jj/discussions/688.
It seems very likely that we're going to remove support for open
commits, but it's still useful to have a `commit` command that lets
the user enter a description and starts a new change. Calling it
`commit` seems good to make the transition from other VCSs simpler.
Use clap::Command::try_get_matches_from() to parse args, and handle the
resulting error as a CommandError. The exit code 2 matches the exit code that
clap would output (this is tested by test_alias).
With this new abstraction, teach the clap output to respect "ui.color"
(--color doesn't seem to be understood). We'll also make use of it when
we route output through the pager.
The "errors" returned by try_get_matches_from() can be found at
https://docs.rs/clap/latest/clap/error/enum.ErrorKind.html. I've tested:
- no args
- --help
- help subcommand
- --version
- subcommand with --help
- subcommand with bad args
This lets us write to stderr, but unlike write_error(), this won't write
with formatting. This will be used for preformatted strings, e.g. those
coming from clap.
Sometimes a tagged commit is not in any branch on the remote, but we
should still consider them upstream and not include them in the
default log template.
This was reported by @colemickens but now that I think about it, I
remember seeing such commits in the Git core repo (v1.4.4.5 and a few
commits before it were never merged into main).
We don't have a good way of testing this because we don't have a
command for creating tags.
Closes#681
This was actually what I meant in my code-review comment but I used
the wrong word ("clip") :) I wasn't going to bother changing it, but
Clippy nightly just started warning about it.
More workspace-derived parameters will be added, and I don't think wrapping
with Option for each makes sense because all parameters should be available
if workspace exists.