This was proposed by @Brixy in
https://github.com/martinvonz/jj/discussions/2882 a while ago. There
seems to be pretty strong consensus that it's a good idea.
I've copied the added test cases from `test_move_command.rs`, just
replacing `move` by `squash`, `--to` by `--into`, and deleting the
test of a no-arg invocation (`jj move` fails, `jj squash` does not -
it defaults to squashing into the parent).
This patch makes `jj squash` us the helper I just extracted from `jj
move`. I had a to add a few small features to it for that.
The `test_squash_command.rs` test changed in a few cases where we do a
partial squash. After this patch, we include the rebased child in the
count of rebased descendants. That seems reasonable and consistent
with partial squash/move further than 1 generation.
This is just a little step towards reusing the helper I just extracted
from `jj move`. I had to update `test_immutable_commits.rs` because it
would otherwise fail because of the merge rather than failing because
of the immutable commit.
I'm soon going to make `jj squash` accept either `-r` or
`--from/--to`, which means `-r` will then be optional. This patch
prepares for that already, since it also simplifies the code a little
(and improves it so we warn if the user does `jj squash -r @
nonexistent`).
We haven't used custom Git commit headers for two main reasons:
1. I don't want commits created by jj to be different from any other
commits. I don't want Git projects to get annoyed by such commit
and reject them.
2. I've been concerned that tools don't know how to handle such
headers, perhaps even resulting in crashes.
The first argument doesn't apply to commits with conflicts because
such commits would never be accepted by a project whether or not they
use custom commit headers. The second argument is less relevant for
conflicted commits because most tools will be confused by such commits
anyway.
Storing conflict information in commit headers means that we can
transfer them via the regular Git wire protocol. We already include
the tree objects nested inside the root-level tree, so they will also
be transferred.
So, let's start by writing the information redundantly to the commit
header and to the existing storage. That way we can roll it back if we
realize there's a problem with using commit headers.
This should address both use cases:
1. If from_relative_path() is directly called, the error says ".." shouldn't
be included in the (normalized) relative path.
2. If parse_fs_path() is used, the error message contains paths relative to
cwd. #3216
There's a caveat: "jj config list -Tname" will concatenate all names in a
single line. That's correct but useless. We might want some option or config
knob to complete missing "\n". This also applies to "log --no-graph".
This moves the config loading closer to CLI args where --tool=<name> option
will be processed. The factory function are proxied through the command helper
so that the base_ignores can be attached there later.
This gets rid of the last UserSettings dependency from edit_diff_external().
I'm going to remove it from edit_diff() too, and let callers pass a
preconfigured MergeTool struct instead.
These changes will make it easier to add --tool=<name> argument #2575.
Before, --tool=:builtin argument was ignored and the tool was loaded from
"ui.diff.tool" option. Since there is no single builtin diff format, :builtin
doesn't make sense here. Maybe we can translate ":<format>" to the internal
diff format instead, but that will also mean "ui.diff.tool" and ".format" can
be merged.
This partially reverts 409356fa5b "merge_tools: enable `:builtin` as default
diff/merge editor."
It was moved to CLI at 42252a2f00 "cli: on `jj init --git-repo=.`, use
relative path to `.git/`." As far as I can tell, .canonicalize() is needed
to calculate relative path, which is now processed differently in
Workspace::init_external_git() and GitBackend::init_external().
The translation from method error to keyword error can go wrong if the context
object had n-ary methods (n > 0), which isn't the case as of now. For
simplicity, arguments error is mapped to "self.<name>(..)" suggestion.
Local variables and "self" could be merged without using extra method, but
we'll need extend_*_candidates() to merge in symbol/function aliases anyway.
This seems more useful if aliases are nested. The innermost error usually
contains the problem, and the outer errors are contexts where aliases are
expanded.
The recovery commit we create when we run into a stale working copy
with a missing operation currently has an empty tree. Our commit
backend at Google creates an index of which files changed in each
commit. That gets really expensive when a commit deletes all files in
the repo, as these recovery commits do. So for our backend, it is much
better to make the recovery commit empty instead. That's what this
patch does.
It almost doesn't matter functionally what tree we use for it since we
don't care much about the current tree when snapshotting the working
copy. It does matter in a few cases, however. One case is for
conflicts. In that case, it's likely better to use the recovery
commit's parent as base tree (as we do by making the recovery commit
empty) than to use an empty tree, as that would guarantee that all
conflicts would be considered resolved. (Side note: perhaps we should
start looking at the current commit's parent instead of looking at the
current commit when snapshotting, but that's a topic for another day.)
When we abandon a working-copy commit, we create a new working-copy
commit on top. This behave is very useful, but it's not obvious. Let's
document it.
Thankfully, 2bbefcc338 (rewrite: default to not simplifying ancestor
merges) means that there are much fewer commands where we need to
document this behavior.
Add an option to list tracked branches only
This option keeps most of the current `--all` printing logic, but:
- Omits local Git-tracking branches by default (can be extended to
support filtering by remote).
- Skip over the branch altogether if it doesn't contain tracked remotes
- Don't print the untracked_remote_refs at the end
Usage:
`jj branch list -t`
`jj branch list --tracked`
`jj branch list --tracked <branch name>`
I think the user usually wants to abandon only newly empty commits. I
think they should use `jj abandon` if they want to get rid of already
empty commits. By keeping already empty commits, we don't need to
special-case the working copy and merge commits.
The description of `jj diff` was lost in commit b5e4e670. We later got
a short description for it in b5e4e670. This patch restores the
original description.
The default immutable_heads() includes tags(), which makes sense, but computing
heads(tags()) can be expensive because the tags() set is usually sparse. For
example, "jj bench revset 'heads(tags())'" took 157ms in my linux stable
mirror. We can of course optimize the heads evaluation by using bit set or
segmented index, but the query includes many historical heads if the repository
has per-release branches, which are uninteresting anyway. So, this patch
replaces heads(immutable_heads()) with trunk().
The reason we include heads(immutable_heads()) is to mitigate the following
problem. Suppose trunk() is the branch to be based off, I think using trunk()
here is pretty good.
```
A B
*---*----* trunk() ⊆ immutable_heads()
\
* C
```
https://github.com/martinvonz/jj/pull/2247#discussion_r1335078879
It should be useful at least in the presentation layer to know which
operations correspond to working-copy snapshots. They might be
rendered differently in the graph, for example. Or maybe an undo
command wants to warn if you just undid a snapshot operation. This
patch just introduces a field in the metadata to store the
information.
I think I prefer this behavior because it's less lossy. The user can
manually simplify the history with `jj rebase -s <merge commit> -d
<one of the parents>` afterwards. We can roll this change back later
if we find it annoying.
We now have lots of tests of ancestor merges in `test_bug_2600()`, so
we don't need the ones in `test_basics()`. Since it doesn't have the
"nottherootcommit" commit, it would break when we change the default
to preserve ancestor merges.
The shortest change id prefix will become a few digits longer, but I think
that's acceptable. Entries included in the "revsets.short-prefixes" set are
unaffected.
The reachable set is calculated eagerly, but this is still faster as we no
longer need to sort the reachable entries by change id. The lazy version will
save another ~100ms in mid-size repos.
"jj log" without working copy snapshot:
```
% hyperfine --sort command --warmup 3 --runs 20 -L bin jj-0,jj-1,jj-2 \
-s "target/release-with-debug/{bin} -R ~/mirrors/linux debug reindex" \
"target/release-with-debug/{bin} -R ~/mirrors/linux \
--ignore-working-copy log -r.. -l100 --config-toml='revsets.short-prefixes=\"\"'"
Benchmark 1: target/release-with-debug/jj-0 -R ~/mirrors/linux --ignore-working-copy log -r.. -l100 --config-toml='revsets.short-prefixes=""'
Time (mean ± σ): 353.6 ms ± 11.9 ms [User: 266.7 ms, System: 87.0 ms]
Range (min … max): 329.0 ms … 365.6 ms 20 runs
Benchmark 2: target/release-with-debug/jj-1 -R ~/mirrors/linux --ignore-working-copy log -r.. -l100 --config-toml='revsets.short-prefixes=""'
Time (mean ± σ): 271.3 ms ± 9.9 ms [User: 183.8 ms, System: 87.7 ms]
Range (min … max): 250.5 ms … 282.7 ms 20 runs
Relative speed comparison
1.99 ± 0.16 target/release-with-debug/jj-0 -R ~/mirrors/linux --ignore-working-copy log -r.. -l100 --config-toml='revsets.short-prefixes=""'
1.53 ± 0.12 target/release-with-debug/jj-1 -R ~/mirrors/linux --ignore-working-copy log -r.. -l100 --config-toml='revsets.short-prefixes=""'
```
"jj status" with working copy snapshot (watchman enabled):
```
% hyperfine --sort command --warmup 3 --runs 20 -L bin jj-0,jj-1,jj-2 \
-s "target/release-with-debug/{bin} -R ~/mirrors/linux debug reindex" \
"target/release-with-debug/{bin} -R ~/mirrors/linux \
status --config-toml='revsets.short-prefixes=\"\"'"
Benchmark 1: target/release-with-debug/jj-0 -R ~/mirrors/linux status --config-toml='revsets.short-prefixes=""'
Time (mean ± σ): 396.6 ms ± 10.1 ms [User: 300.7 ms, System: 94.0 ms]
Range (min … max): 373.6 ms … 408.0 ms 20 runs
Benchmark 2: target/release-with-debug/jj-1 -R ~/mirrors/linux status --config-toml='revsets.short-prefixes=""'
Time (mean ± σ): 318.6 ms ± 12.6 ms [User: 219.1 ms, System: 94.1 ms]
Range (min … max): 294.2 ms … 333.0 ms 20 runs
Relative speed comparison
1.85 ± 0.14 target/release-with-debug/jj-0 -R ~/mirrors/linux status --config-toml='revsets.short-prefixes=""'
1.48 ± 0.12 target/release-with-debug/jj-1 -R ~/mirrors/linux status --config-toml='revsets.short-prefixes=""'
```
When adding a new workspace, I would expect that it inherits the
patterns from the workspace I ran the command in. We currently don't
do that. That's quite annoying when your repo has very many files
(like at Google).
The `ContentHash` documentation specifies that implementations for enums should
hash the ordinal number of the variant contained in the enum as a 32-bit
little-endian number and then hash the contents of the variant, if any.
The current implementations for `std::Option`, `MergedTreeId`, and
`RemoteRefState` are non-conformant since they hash the ordinal number as a u8
with platform specific endianness.
Fixes#3051
The legacy parsing rules are turned into compatibility errors. The x:y rule
is temporarily enabled when parsing string patterns. It's weird, but we can't
isolate the parsing function because a string pattern may be defined in an
alias.
This adds a config to render a synthetic node with a "(elided
revisions)" description for elided segments of the graph.
I didn't add any templating support for the elided nodes because I'm
not sure how we would want that to work. In particular, I don't know
what `commit_id` and most other keywords should return for elided
revisions.
Users who edit non-head commits usually expect `jj next/prev` to
continue to edit the next/previous commit, so let's make that the
default behavior. This should not confuse users who don't edit
non-head commits since they will simply not be in this state. My main
concern is that doing `jj next; jj prev` will now usually take you
back to the previous commit, but not if you started on the parent of a
head commit.
The main goal is to avoid having a symlink in our source tree. Currently, there
is no good way to work with the `jj` repo with `jj` on Windows. Currently `jj`
just crashes with symlinks. This is being worked on, see e.g. #2939, but it will
always depend on whether Developer Mode is enabled in Windows or whether
symlinks are materialized as text files with symlinks. Finally, MkDocs has
trouble following symlinks on Windows, so building docs wouldn't work there.
Another advantage is that, previously, we were lucky that MkDocs treats `insta`
header in `cli-reference@.md.snap` as a Markdown header and follows symlinks at
all. Now, we no longer depend on that.
If the operation corresponding to a workspace is missing for some reason
(the specific situation in the test in this commit is that an operation
was abandoned and garbage-collected from another workspace), currently,
jj fails with a 255 error code. Teach jj a way to recover from this
situation.
When jj detects such a situation, it prints a message and stops
operation, similar to when a workspace is stale. The message tells the
user what command to run.
When that command is run, jj loads the repo at the @ operation (instead
of the operation of the workspace), creates a new commit on the @
commit with an empty tree, and then proceeds as usual - in particular,
including the auto-snapshotting of the working tree, which creates
another commit that obsoletes the newly created commit.
There are several design points I considered.
1) Whether the recovery should be automatic, or (as in this commit)
manual in that the user should be prompted to run a command. The user
might prefer to recover in another way (e.g. by simply deleting the
workspace) and this situation is (hopefully) rare enough that I think
it's better to prompt the user.
2) Which command the user should be prompted to run (and thus, which
command should be taught to perform the recovery). I chose "workspace
update-stale" because the circumstances are very similar to it: it's
symptom is that the regular jj operation is blocked somewhere at the
beginning, and "workspace update-stale" already does some special work
before the blockage (this commit adds more of such special work). But it
might be better for something more explicitly named, or even a sequence
of commands (e.g. "create a new operation that becomes @ that no
workspace points to", "low-level command that makes a workspace point to
the operation @") but I can see how this can be unnecessarily confusing
for the user.
3) How we recover. I can think of several ways:
a) Always create a commit, and allow the automatic snapshotting to
create another commit that obsoletes this commit.
b) Create a commit but somehow teach the automatic snapshotting to
replace the created commit in-place (so it has no predecessor, as viewed
in "obslog").
c) Do either a) or b), with the added improvement that if there is no
diff between the newly created commit and the former @, to behave as if
no new commit was created (@ remains as the former @).
I chose a) since it was the simplest and most easily reasoned about,
which I think is the best way to go when recovering from a rare
situation.
These operator symbols are different from the ones in the revset language. I
have no idea if we need bitwise operators, but we'll probably add comparison
operators. It would look weird if 'x == y & z' were parsed as '(x == y) & z'.
this greatly speeds up the time to run all tests, at the cost of slightly larger recompile times for individual tests.
this unfortunately adds the requirement that all tests are listed in `runner.rs` for the crate.
to avoid forgetting, i've added a new test that ensures the directory is in sync with the file.
## benchmarks
before this change, recompiling all tests took 32-50 seconds and running a single test took 3.5 seconds:
```
; hyperfine 'touch lib/src/lib.rs && cargo t --test test_working_copy'
Time (mean ± σ): 3.543 s ± 0.168 s [User: 2.597 s, System: 1.262 s]
Range (min … max): 3.400 s … 3.847 s 10 runs
```
after this change, recompiling all tests take 4 seconds:
```
; hyperfine 'touch lib/src/lib.rs ; cargo t --test runner --no-run'
Time (mean ± σ): 4.055 s ± 0.123 s [User: 3.591 s, System: 1.593 s]
Range (min … max): 3.804 s … 4.159 s 10 runs
```
and running a single test takes about the same:
```
; hyperfine 'touch lib/src/lib.rs && cargo t --test runner -- test_working_copy'
Time (mean ± σ): 4.129 s ± 0.120 s [User: 3.636 s, System: 1.593 s]
Range (min … max): 3.933 s … 4.346 s 10 runs
```
about 1.4 seconds of that is the time for the runner, of which .4 is the time for the linker. so
there may be room for further improving the times.
When the user doesn't have a configured default command, we show a
hint saying to set `ui.default-command`. I think the user is very
likely to want to set that in the user-wide config, so let's include
the command in the hint.
This reimplements the change 9faa4670d5 "cli: on init, import git refs prior
to importing HEAD." Initialization is special because the HEAD ref isn't
available to jj yet, and there is an empty working-copy commit.
The initialization function could be refactored to go through the common code
path, but I think doing that would make future improvement harder. We might
want to initialize tracking branches based on .git/config for example.
Fixes#2942
This initializes a git backed repo.
* It does the same thing as `jj init --git` except that it
has a --colocated flag to explicitly specify that we want
the .git repo to be side-by-side the .jj repo in the working
directory.
* `jj init --git` will keep the current behaviour and will not
be able to create colocated git backed repos.
* Update test snapshots.
A few minor updates after 0f27152 AKA #2945
Specifically, I tried to change the help text so that it looks better as
Markdown. I also reworded the deprecation warning and added a hint.
this has two main advantages:
- it makes it clear that the shells are mutually exclusive
- it allows us to extend the command with shell-specific options in the future if necessary
as a happy accident, it also adds support for `elvish` and `powershell`.
for backwards compatibility, this also keeps the existing options as hidden flags.
i am not super happy with how the new help looks; the instructions for setting up the shell are
squished together and IMO a little harder to read. i'm open to suggestions.
it's somewhat confusing to me that the `--bash` flag exists at all, since it does nothing - maybe it makes sense to give a hard error? or just remove the flag?
but in any case, it seems good to document the existing behavior.
previously, `jj diff` would show the full contents of binary files such as images.
after this change, it instead shows "(binary)". it still shows the filename and metadata so that
users can open the file in the viewer of their choce.
future work could involve showing binary files as Sixel or similar; finding a way to compare large
non-binary files without filling up the screen; or extending the data backends to avoid having to
read the whole file contents into memory.
Previously, `jj git push; jj git push` would tell the user that "No
branches point to the specified revisions.". I found this confusing,
even though strictly speaking it is correct (as the default revset only
considers revisions that haven't been pushed to the remote).
Closes#2241
The error output gets more verbose because all gix error sources are printed.
Maybe we'll need a better formatting, but changing to multi-line output doesn't
look nice either.
Summary: As discussed in Discord, on GitHub, and elsewhere, this change
deprecates the use of `jj merge` and suggests users use `jj new` exclusively
instead. `merge` isn't completely unfit as a name; but we think it obscures
the generality of `new` and we want people to use it instead.
To further drive the bit home, by default, `jj merge` is now hidden. This will
hopefully stop new users from running into it.
Signed-off-by: Austin Seipp <aseipp@pobox.com>
Change-Id: I94938aca9d3e2aa12d1394a5fbc58acce3185b56
Summary: As discussed in Discord, on GitHub, and elsewhere, this change
deprecates the use of `jj checkout` and suggests users use `jj new` exclusively
instead. The verb `checkout` is a relic of a bygone era the — days of RCS
file locking, before 3-way merge — and is not a good, fitting name for the
functionality it provides.
To further drive the bit home, by default, `jj checkout` (and `jj co`) is now
hidden. This will hopefully stop new users from running into it.
Signed-off-by: Austin Seipp <aseipp@pobox.com>
Change-Id: I7a1adfc9168fce1f25cf5d4c4c313304769e41a1
These didn't really need to use `jj checkout`, and it will be deprecated in a
future change anyway. Move them out of there and into `new`.
Ideally this would go into `test_conflicts.rs`, but that exists in `jj-lib`, so
it doesn't have `TestEnvironment` available to it.
Signed-off-by: Austin Seipp <aseipp@pobox.com>
Change-Id: If0173b27ab4d1f6036a4ec632ec77b6824f310c3
This prevents rust doctests from trying to compile the code blocks.
I don't think we use doctests much or at all, but I'd like
`cargo insta test --test-runner nextest --accept --workspace`
to pass.
Unfortunately, this affects the output of `jj help` as well, but I
can't think of a workaround for that.
I am using a very hacky approach, using `insta` to generate the markdown help.
This is based on a lucky coincidence that `insta` chose to use a header
format for snapshot files that MkDocs interprets as a Markdown header (and
ignores).
I considered several other approaches, but I couldn't resist the facts that:
- This doesn't require new developers to install any extra tools or run any
extra commands.
- There is no need for a new CI check.
- There is no need to compile `jj` in the "Make HTML docs" GitHub action,
which is currently very fast and cheap.
Downside: I'm not sure how well MkDocs will work on Windows, unless the
developer explicitly enables symbolic links (which IIUC is not trivial).
### Possible alternatives
My next favorite approaches (which we could switch to later) would be:
#### `xtask`
Set up a CI check and a [Cargo `xtask`] so that `cargo xtask cli-help-to-md`
essentially runs `cargo run -- util markdown-help > docs/cli-reference.md` from
the project root.
Every developer would have to know to run `cargo xtask cli-help-to-md` if
they change the help text.
Eventually, we could have `cargo xtask preflight` that runs `cargo +nightly
fmt; cargo xtask cli-help-to-md; cargo nextest run`, or `cargo insta`.
#### Only generate markdown for CLI help when building the website, don't track it in Git.
I think that having the file in the repo will be nice to preview changes to
docs, and it'll allow people to consult the file on GitHub or in their repo.
The (currently) very fast job of building the website would now require
installing Rust and building `jj`.
#### Same as the `xtask`, but use a shell script instead of an `xtask`
An `xtask` might seem like overkill, since it's Rust instead of a shell script.
However, I don't want this to be a shell script so that new contributors on
Windows can still easily run it ( since this will be necessary for the CI to
pass) without us having to support a batch file.
#### Cargo Alias
My first attempt was to set up a [cargo alias] to run this, but that doesn't
support redirection (so I had to change the `jj util` command to output to a
file) and, worse, is incapable of executing the command *in the project root*
regardless of where in the project the current directory is. Again, this seems
to be too inconvenient for a command that every new PR author would have to run
to pass CI.
Overall, this just seems a bit ugly. I did file
https://github.com/rust-lang/cargo/issues/13348, I'm not really sure that was
worthwhile, though.
**Aside:** For reference, the alias was:
```toml
# .cargo/config.toml
alias.gen-cli-reference = "run -p jj-cli -- util markdown-help docs/cli-reference.md"
```
### Non-alternatives
#### Clap's new feature
`clap` recently obtained a similarly-sounding feature in
https://github.com/clap-rs/clap/pull/5206. However, it only prints short help
for subcommands and can't be triggered by an option AFAICT, so it won't help us
too much.
[Cargo `xtask`]: https://github.com/matklad/cargo-xtask
[cargo alias]: https://doc.rust-lang.org/cargo/reference/config.html#alias
The "::HEAD" set is usually smaller than "::git_refs". If these sets were
imported in that order, "HEAD..git_refs" commits would be indexed on top of
the "::HEAD" commits. It's not a problem, but undesirable.
This is a convenient command, for scripting things like `cd $(jj root)
&& do something`, and it seems better to allow people to find it
before they learn about workspaces.
The count() function in this trait is used by "jj branch" to determine
(and then report) how many commits a certain branch is ahead/behind
another branch. This is currently implemented by walking all commits
in the revset, counting how many were encountered. But this could be
improved: if the number is large, it is probably sufficient to report
"at least N" (instead of walking all the way), and this does not scale
well to jj backends that may not have all commits present locally (which
may prefer to return an estimate, rather than access the network).
Therefore, add a function that is explicitly documented to be O(1)
and that can return a range of values if the backend so chooses.
Also remove count(), as it is not immediately obvious that it is an
expensive call, and callers that are willing to pay the cost can obtain
the exact same functionality through iter().count() anyway. (In this
commit, all users of count() are migrated to iter().count() to preserve
all existing functionality; they will be migrated to count_estimate() in
a subsequent commit.)
"branch" needed to be updated due to this change. Although jj
is currently only available in English, I have attempted to keep
user-visible text from being assembled piece by piece, so that if we
later decide to translate jj into other languages, things will be easier
for translators.
It seems obvious in hindsight to have a virtual root operation just
like we have a virtual root commit. It removes the same kind of
problems by making sure there's always a common ancestor (or multiple)
between any two commits.
I think the reason I didn't add a root operation from the beginning
was that there used to be a mandatory working-copy commit in the view
(this was before support for multiple workspaces).
Perhaps we should remove the "initialize repo" operation now. The only
difference between their view objects is that the "initialize repo"
operation adds the root commit as a head. We could add that to the
root operation, but then the root operation's value depends on the
commit backend.
We've had the public_heads for as long as we've had the View object,
IIRC (I didn't check), but we still don't use it for anything. I don't
have any concrete plans for using it either. Maybe our config for
immutable commits is good enough, or maybe we'll want something more
generic (like Mercurial's phases). For now, I think we should simplify
by removing it the storage for public heads.
When debugging behavior of badly-GCed repos, I find it's annoying that "op log"
fails because the index can't be loaded. Since "op log" doesn't need a repo, I
think it's better to display the exact op-heads state without merging.
If indexing failed due to missing commit objects, the repo won't be loadable
without --ignore-working-copy (at least in colocated environment.) In that
case, we can use "op abandon" to recover, but we had to work around the failed
index loading by --ignore-working-copy. Since "op abandon" isn't a repo-level
command, it's better to bypass working-copy snapshot and import of git refs at
all.
--at-op is rejected because it's useless and we'll need extra care for "@"
expression resolution and working-copy updates.
The formatting is closer to hg than git just because it's easier to
conditionalize the whole line per keyword. Our template language doesn't
have infix logical operators.
Unlike the other default templates, all remote branches are displayed because
it's "detailed" output.
Closes#2509
It doesn't make sense to do gc from a non-head operation because that means
either the head operation would be corrupted or the --at-op argument is
ignored.
I'm going to add try_from_hex(), which requires Self: Sized. Such trait bound
could be added, but I don't think we'll need abstracted ObjectId constructors
at all.
I'm going to add a prefix resolution method to OpStore, but OpStore is
unrelated to the index. I think ObjectId, HexPrefix, and PrefixResolution can
be extracted to this module.
In order to implement GC (#12), we'll need to somehow prune old operations.
Perhaps the easiest implementation is to just remove unwanted operation files
and put tombstone file instead (like git shallow.) However, the removed
operations might be referenced by another jj process running in parallel. Since
the parallel operation thinks all the historical head commits are reachable, the
removed operations would have to be resurrected (or fix up index data, etc.)
when the op heads get merged.
The idea behind this patch is to split the "op log" GC into two steps:
1. recreate operations to be retained and make the old history unreachable,
2. delete unreachable operations if the head was created e.g. 3 days ago.
The latter will be run by "jj util gc". I don't think GC can be implemented
100% safe against lock-less append-only storage, and we'll probably need some
timestamp-based mechanism to not remove objects that might be referenced by
uncommitted operation.
FWIW, another nice thing about this implementation is that the index is
automatically invalidated as the op id changes. The bad thing is that the
"undo" description would contain an old op id. It seems the performance is
pretty okay.
This removes CommandError dependency from these resolution functions. We might
want to refactor the error types again if we introduce a real "opset" evaluator.
The error message for unresolved op heads now includes "@" instead of the whole
expression.
At some point, I tried `new_commit` instead of `rewrite_commit` in the split
command. That seemed to work, but messed up the dates in a subtle way.
This commit should prevents repeats of this mistake and emphasize the
importance of the author dates being preserved.
This partially reverts 6c627fb30d "cli: default to log when no subcommand is
provided." We could reject an empty alias at all, but we would still need to
ensure that the expanded alias contained a subcommand name.
The help output is a bit odd as the <COMMAND> can be omitted, but I think
that's acceptable. If we do care about that, maybe we can override_usage().
This is basically the same as Mercurial's workaround. I don't know about Git,
but arguments order is very restricted in git, so -C path can be parsed prior
to alias expansion. In hg and jj, doing that would be messy and unreliable.
Closes#2414
It should be better to handle invalid -R path globally. The error message is
a bit worse, but I think it's still okay.
This helps to load temporary config from the cwd-relative path. If the command
processing continued with an invalid -R path, the temporary config would have
to be explicitly discarded.
As far as I can see in the chat, there's no objection to changing the default,
and git.auto-local-branch = false is generally preferred.
docs/branches.md isn't updated as it would otherwise conflict with #2625. I
think the "Remotes" section will need a non-trivial rewrite.
#1136, #1862
This is really a simple change that does the following in a transaction:
* Set the new branch name to point to the same commit as the old branch name.
* Set the old branch name to point to no commit (hence deleting the old name).
Before it starts, it confirms that the new branch name is not already in use.
I originally thought this would be unavoidable, but I was wrong. "jj git clone"
doesn't implicitly create any local branch if git.auto-local-branch is off, and
that's fine because the detached HEAD state is normal in jj.
That being said, Git users would expect that the main/master branch exists.
Since importing the default branch is harmless, let's create and track it no
matter if git.auto-local-branch is off.
This tries to clarify the fact that the branches must be remote and the syntax
for specifying them as globs.
Cc @yuja, https://github.com/martinvonz/jj/pull/2625#discussion_r1423379351
Here is the result (excerpt):
```
$ jj branch track --help
Start tracking given remote branches
A tracking remote branch will be imported as a local branch of the same name. Changes to it
will propagate to the existing local branch on future pulls.
Usage: jj branch track [OPTIONS] <BRANCH@REMOTE>...
Arguments:
<BRANCH@REMOTE>...
Remote branches to track
By default, the specified name matches exactly. Use `glob:` prefix to select
branches by wildcard pattern. For details, see
https://github.com/martinvonz/jj/blob/main/docs/revsets.md#string-patterns.
Examples: branch@remote, glob:main@*, glob:jjfan-*@upstream
```
This prints a hint about using `jj new <first conflicted commit>` and
`jj squash` to resolve conflicts. The hint is printed whenever there
are new or resolved conflicts.
I hope this hint will be useful especially for new users so they know
which commit to resolve conflicts in first. It may not be obvious that
they should start with the bottommost one. I hope the hint will also
be useful for more more experienced user by letting them just copy the
printed command without first running `jj log` to find the right
commit..
When e.g. `jj rebase` results in new conflicts, it's useful for the
user to learn about that without having to run `jj log` right
after. This patch adds reporting of new conflicts created by an
operation. It also add reporting of conflicts that were resolved or
abandoned by the operation.
There was no measurable performance impact when rebasing a single
commit in the Linux kernel repo.
Before, an absolute path would be saved in the git_target file if .git is a
symlink. That's not wrong, but seemed a bit weird. Let's consolidate the
behavior across .git file types.
Apparently, libgit2 doesn't deduce "core.bare" config from the directory name,
but gitoxide implements it correctly. So we shouldn't blindly canonicalize
the Git repository path. Fortunately, the saved git_target path isn't a fully-
canonicalized form (unless user explicitly sepcified "--git-repo ./.git"), so
we don't need a hack to remap git_target back to the symlink path.
is_colocated_git_workspace() is adjusted since the git_workdir is no longer
resolved from the fully-canonicalized repo path, at least in our code. Still we
have the ".git/.." fallback because test_init_git_colocated_symlink_gitlink()
would otherwise fail. I haven't figured out why, and the test might be actually
wrong compared to the git CLI behavior, but let's not change that for now.
Fixes#2668
A git repo created by the "repo" tool doesn't have core.base set, which means
the "bare"-ness relies on the directory name. Gitoxide appears to parse it
correctly, whereas libgit2 doesn't. That's why the symlinked .git repo is no
longer processed as a colocated repo.
#2668
See comments inline for details. Cc #2600.
In particular, I wanted to make sure these behaviors are not affected by #2646.
They don't seem to be.
The tests ended up weirder than expected because of
https://github.com/martinvonz/jj/issues/2600#issuecomment-1835418824. Even
though, right now, the behavior of tests is unaffected by that issue, the
*expected* behavior is different.
Branches move around a little confusigly with `abandon`. We do want to keep
them, to test their behavior, but we can show the change id to make things
clearer.
Note that one of the new tests panics; this is a newly discovered bug.
In Git, a commit's direct parent is allowed to also be an indirect ancestor
at the same time. `jj` currently tries to prevent this situation, but does
allow it. The correctness of `rebase -r A -d descendant_of_A` currently depends
on this jj-specific behavior; we should change that.
Cc #2600
Allowing `jj init --git` in an existing Git repo creates a second Git
store in `.jj/repo/store/git`, totally disconnected from the existing
Git store. This will only produce extremely confusing bugs for users,
since any operations they make in Git will *not* be reflected in the
jj repo.
This follows up on 3967f63 (see that commit's description for more
motivation) and e79c8b6.
In a discussion linked below, it was decided that forbidding `-r --skip-empty`
entirely is preferable to the mixed behavior introduced in 3967f63.
3967f637dc (commitcomment-133539911)
If the existing git repo contains local and remote branches of the same name,
one of the remote branches is probably a tracking remote branch. Let's show
a hint how to set up tracking branches. The tracking state could be derived
from .git/config, but doing that automatically might cause another issue like
#1862, which could have been mitigated by git.auto-local-branch = false.
Repeating these is a no-op. This allows:
```shell
jj new -r a -r b # Equivalent to jj new a b
jj new --before a --before b # Equivalent to jj new a b --before
```
I keep typing the latter and getting an annoying error.
Per discussion in https://github.com/martinvonz/jj/discussions/2555. I'm
okay with either way, but it's confusing if we had "branch create" and
"branch set" and both of these could create a new branch.
Renamed `description_template_for_commit` to
`description_template_for_describe` since it's only used in
`cmd_describe`.
Renamed `description_template_for_cmd_split` to
`description_template_for_commit` and modified to accomodate empty
`intro` argument.
Fixes#2439.
As discussed in Discord, it's less useful if remote_branches() included
Git-tracking branches. Users wouldn't consider the backing Git repo as
a remote.
We could allow explicit 'remote_branches(remote=exact:"git")' query by changing
the default remote pattern to something like 'remote=~exact:"git"'. I don't
know which will be better overall, but we don't have support for negative
patterns anyway.
Summary: A natural extension of the existing support, as suggested by Scott
Olson. Closes#2496.
Signed-off-by: Austin Seipp <aseipp@pobox.com>
Change-Id: I91c9c8c377ad67ccde7945ed41af6c79
My gut feeling is that gitoxide aims to be more transparent than libgit2. We'll
need to know more about the underlying Git data model.
Random comments on gix API:
* gix::Repository provides API similar to git2::Repository, but has less
"convenient" functions. For example, we need to use .find_object() +
.try_to/into_<kind>() instead of .find_<kind>().
* gix::Object, Blob, etc. own raw data as bytes. gix::object and gix::objs
types provide high-level views on such data.
* Tree building is pretty low-level compared to git2.
* gix leverages bstr (i.e. bytes) extensively.
It's probably not difficult to migrate git::import/export_refs(). It might
help eliminate the startup overhead of libssl initialization. The gix-based
GitBackend appears to be a bit faster, but that wouldn't practically matter.
#2316
Like "jj log PATHS...", unmatched name isn't an error. I don't think
"jj branch list glob:'push-*'" should fail just because there are no in-flight
PR branches.
Makes sure that, at the point the commit summary for the new commit is written,
the original commit that is being rewritten is already abandoned. Otherwise,
once we show divergent change ids (in a subsequent commit) in the short commit
template, the commits would be shown as divergent.
This also has an effect on whether branches are displayed next to the commit;
the changes in test_resotre_command happen because, now, the branch is properly
propagated to the restored commit before its summary is displayed.
test_templater_alias(), test_templater_alias_override(), and
test_templater_bad_alias_decl() aren't moved since they also test config loading
and error formatting. The first test in test_templater_parse_error() is left for
the same reason. test_templater_upper_lower() depends on the commit templater.
I don't think many of the tests in test_templater.rs should use "jj log" command
as they check very specific template syntax and function behaviors. Let's move
them to in-module tests. We could add a separate test file, but we would have
to export a couple of templater macros.
test_templater_timestamp_method() is migrated as example.
Since "jj git fetch --branch" supports glob patterns, users would expect that
"jj git push --branch glob:.." also works.
The error handling bits are copied from "branch" sub commands. We might want to
extract it to a common helper function, but I haven't figured out a reasonable
boundary point yet.
AFAICT, all callers of `Merge::to_file_merge()` are already well
prepared for working with executable files. It's called from these
places:
* `local_working_copy.rs`: Materialized conflicts are correctly
updated using `Merge::with_new_file_ids()`.
* `merge_tools/`: Same as above.
* `cmd_cat()`: We already ignore the executable bit when we print
non-conflicted files, so it makes sense to also ignore it for
conflicted files.
* `git_diff_part()`: We print all conflicts with mode "100644" (the
mode for regular files). Maybe it's best to use "100755" for
conflicts that are unambiguously executable, or maybe it's better to
use a fake mode like "000000" for all conflicts. Either way, the
current behavior seems fine.
* `diff_content()`: We use the diff content in various diff
formats. We could add more detail about the executable bits in some
of them, but I think the current output is fine. For example,
instead of our current "Created conflict in my-file", we could say
"Created conflict in executable file my-file" or "Created conflict
in ambiguously executable file my-file". That's getting verbose,
though.
So, I think all we need to do is to make `Merge::to_file_merge()` not
require its inputs to be non-executable.
Closes#1279.
I'm about to make conflicts also get materialized in executable
files. We'll lose some of the test coverage in `test_chmod_command.rs`
then, because the those tests rely on the materialized content to
describe the executable bits. So this commit adds a debug command for
printing tree values and uses that in the tests.
If we add glob support, users will probably want to do something like
'jj branch untrack glob:"*@origin"'. It would be annoying if the command
failed just because one of the remote branches has already been untracked.
Since branch tracking/untracking is idempotent, it's safe to continue in
those cases.
The parse rule is lax compared to revset. We could require the pattern to be
quoted, but that would mean glob patterns have to be quoted like 'glob:"foo*"'.
I personally don't mind if "jj branch list" showed all non-tracking branches,
but I agree it would be a mess if ~500 remote branches were listed. So let's
hide them by default as non-tracking branches aren't so interesting.
Closes#1136
This will be the option to include non-tracking remote branches. We could add
more fine-grained filtering flags, but I think --all is good enough and easier
to remember.
This patch also updates many of the test outputs to include synchronized remote
branches. I think verbose outputs will help catch future bugs.
This replaces our existing mechanism of adding `/.jj/` to
`.git/info/exclude` by adding `*` to `.jj/.gitignore`, as suggested by
@ppwwyyxx. That simplifies the code quite a bit, and it avoids the
problem with `.git/info/exclude` not existing (it apparently doesn't
exist when the user uses
https://git-scm.com/docs/git-init#_template_directory).
Closes#2385.
We can provide more actionable error message than "not fast-forwardable". If
the push was fast-forwardable, "jj branch track" should be able to merge the
remote branch without conflicts, so the added step would be minimal.
Although this is logically correct, the error message is a bit cryptic. It's
probably better to reject push if non-tracking remote branches exist.
#1136
This means that the commits previously pinned by remote branches are no longer
abandoned. I think that's more correct since "push" is the operation to
propagate local view to remote, and uninteresting commits should have been
locally abandoned.
Since I'm going to make git::push_branches() update the repo view internally,
it should fail fast if the remote name is reserved. Before, the problem was
detected on git::import_refs().
This patch adds MutableRepo::track_remote_branch() as we'll probably need to
track the default branch on "jj git clone". untrack_remote_branch() is also
added for consistency.
I'm not sure if this is the best way to render non-tracking branches, but
it helps to write CLI tests. Maybe we can add some hint or decoration to
non-tracking branches, but I'd like to avoid bikeshedding at this point.
Since we haven't migrated the push function yet, a deleted branch can be
pushed to non-tracking remotes. This will be addressed later.
#1136
Summary: Yuya's changes and mine had a semantic conflict ("merge skew") between
the two of them, as b7c7b19e changed the `op log `output slightly, whereas
220292ad included a new test that used `op log` itself.
Generated by `cargo insta review`.
Signed-off-by: Austin Seipp <aseipp@pobox.com>
Change-Id: I51d4de7316b1abc09be4f9fa0dd0d1a1
We could fix do_git_clone() instead, but it seemed a bit weird that the
git_repo_path is relative to the store path which is unknown to callers.
Fixes#2374
There's a subtle behavior change. Unlike the original remove_remote_branch(),
remote_views entry is not discarded when the branches map becomes empty. The
reasoning here is that the remote view can be added/removed when the remote
is added/removed respectively, though that's not implemented yet. Since the
serialized data cannot represent an empty remote, such view may generate
non-unique content hash.
Summary: This allows `workspace forget` to forget multiple workspaces in a
single action; it now behaves more consistently with other verbs like `abandon`
which can take multiple revisions at one time.
There's some hoop-jumping involved to ensure the oplog transaction description
looks nice, but as they say: small conveniences cost a lot.
Signed-off-by: Austin Seipp <aseipp@pobox.com>
Change-Id: Id91da269f87b145010c870b7dc043748
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
Before this patch, it was an error to run `jj config set --user foo
'[1]'` twice. But it's only been broken since the previous commit
because '[1]' was interpreted as a string before then.
Now we have a separate map for "git" tracking remote, we can always preserve
the last imported/exported git_refs. The option to restore git-tracking refs
has been removed. Perhaps, --what can be reorganized as --local and --remote
<NAME>.
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.
On my Debian laptop, openssl_init() takes ~30ms to load the default CA
certificates serialized in PEM format, and the cost is added to each jj
invocation. This change saves 20s (of 50s) on my machine.
% wc -l /usr/lib/ssl/cert.pem
3517 /usr/lib/ssl/cert.pem
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.
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`).
As we can set HEAD to an arbitrary ref by using .reference_symbolic(), we don't
have to manage a ref that can also be valid as a branch name.
Fixes#1495
Apparently, it gets too verbose if the remote history is actively rewritten.
Let's summarize the output for now. The plan is to show the list of moved refs
instead of the full list of abandoned commits.
The problem is that the first non-working-copy commit moves the unborn current
branch to that commit, but jj doesn't "export" the moved branch. Therefore,
the next jj invocation notices the "external" ref change, which was actually
made by jj.
I'm not sure why we play nice by setting the "current" HEAD, but I *think* it's
okay to set the "new" HEAD and reset to the same commit to clear Git index.
This will probably help to understand why you've got conflicts after fetching.
Maybe we can also report changed local refs.
I think the stats should be redirected to stderr, but we have many other similar
messages printed to stdout. I'll probably fix them all at once later.
I think most users who change the set of immutable heads away from
`trunk() | tags()` are going to also want to change the default log
revset to include the newly mutable commit and to exclude the newly
immutable commits. So let's update the default log revset to use
`immutable_heads()` instead.
`test_templater` changed because we have overridden the set of
immutable commits there so `jj log` now includes the remote branch.
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).
I'm going to make this function check against a configurable revset
indicating immutable commits. It's more efficient to do that by
evaluating the revset only once.
We may want to have a version of the function where we pass in an
unevaluated revset expression. That would allow us to error out if the
user accidentally tries to rebase a large set of commits, without
having to evaluate the whole set first.
Once we add support for immutable commits, `jj duplicate` should be
allowed to create duplicate of them. The reason it can't duplicate the
root commit is that it would mean there would be multiple root
commits, which would break the invariant that the single root commit
is the only root commit (and the backends refuse to write a commit
without parents). So let's have `jj duplicate` check specifically that
the user doesn't try to duplicate the root commit instead.
Since we have overloaded operator symbols, we need to deduplicate them
upfront. Legacy and compat operators are also removed from the suggestion.
It's a bit ugly to mutate the error struct before calling Error::renamed_rule(),
but I think it's still better than reimplementing message formatting function.