The expression 'x ~ empty()' is identical to 'x & file(".")', but more
intuitive.
Note that 'x ~ empty()' is slower than 'x & file(".")' since the negative
intersection isn't optimized right now. I think that can be handled as
follows: 'x ~ filter(f)' -> 'x & filter(!f)' -> 'filter(!f, x)'
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.
This migrates the native backend from Protobuf to Thrift since
Google's Protobuf team does let us import jj into Google's monorepo if
it uses a third-party Protobuf library.
Since the native backend is not supported, I didn't write any
migration code for it.
We can't remove `lib/src/protos/store.proto` yet, because it's also
used by the Git backend (only the `predecessors` and `change_id`
fields).
When we export branches to Git, we didn't update our own record of
Git's refs. This frequently led to spurious conflicts in these refs
(e.g. #463). This is typically what happened:
1. Import a branch pointing to commit A from Git
2. Modify the branch in jj to point to commit B
3. Export the branch to Git
4. Update the branch in Git to point to commit C
5. Import refs from Git
In step 3, we forgot to update our record of the branch in the repo
view's `git_refs` field. That led to the import in step 5 to think
that the branch moved from A to C in Git, which conflicts with the
internal branch target of B.
This commit fixes the bug by updating the refs in the `MutableRepo`.
Closes#463.
As mentioned in the previous commit, we need to remove the Protobuf
dependency in order to be allowed to import jj into Google's
repo. This commit makes `SimpleOpStore` store its data using Thrift
instead of Protobufs. It also adds automatic upgrade of existing
repos. The upgrade process took 18 s in my repo, which has 22k
operations. The upgraded storage uses practically the same amount of
space. `jj op log` (the full outut) in my repo slowed down from 1.2 s
to 3.4 s. Luckily that's an uncommon operation. I couldn't measure any
difference in `jj status` (loading a single operation).
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
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.
If you remove all refs from the backing Git repo and then run `jj git
import`, we would see that all commits disappeared from the Git repo,
so we would remove them from the jj repo too. However, we do that by
doing a history walk from old heads to the new heads, which includes
the root commit when the new heads is an empty set. That means that we
mark the root commit as abandoned, which led to a crash in
`rewrite.rs` (when we try pick the root commit's first parent to use
as parent for rebased commits).
I was trying to create a reproduction script for #412, but the script
ran into another bug first. The script removed all the local and
remote branches from the backing Git repo. I noticed that we would
then try to abandon all commits. We should still count Git HEAD's
target as visible and not try to abandon it. This patch fixes that.
Since 'merges()' just filters the candidates set per item, it doesn't need
a candidates argument. Perhaps, 'merges(x)' could be a predicate to select
merge commits within a subgraph 'x', but I don't know if that would be
useful.
In the current implementation, tree is diffed twice if both PATH and -p
are specified. If this adds significant cost, we'll need to reimplement
it without using a revset abstraction (or maybe adjust revset/graph API.)
Add the `jj interdiff` command for comparing only the diffs of commits.
Its args are identical to that of `jj diff`, minus `--revision` (because
interdiff always requires two commits).
Like `jj obslog -p`, Changes introduced by intervening commits are
ignored by rebasing `--from` onto `--to` 's parents.
`jj merge` just creates an empty change, which is practically the same
as `jj new`. The main difference is that the former requires more than
one argument and the latter requires at most one argument. It seems
cleaner to generalize them and make them aliases. This patch starts
doing that by making `jj new` accept more than one argument.
Instead of having `jj merge` be exactly an alias for `jj new`, we may
want to make it a thin wrapper that just checks that more than one
argument was given. That would probably be less confusing to users who
run `jj merge` without arguments to see what it does.
We should probably make `jj checkout` also be an alias for `jj new`,
but that will have to wait until we have removed support for open
commits (since `jj checkout` still has logic for dealing with open
commits).
I suppose it could be seen as a bug fix that we no longer discard a
description without asking, but it was intentionally done the way it
was before.
While at it, I also clarified that the source commit gets abandoned if
it becomes empty.
In 8ae9540f2c, I made `jj move/squash/unsquash` not abandon the
working copy if it became empty because that would lose any
description associated with it. It turned out that the new behavior
was also confusing because it made it unclear if the working-copy
commit was actually abandoned. Let's roll back that change and instead
ask the user for a combined description when both the source and
destination commits have non-empty descriptions. Not discarding a
non-empty description seems like a good improvement regardless of the
behavior related to working-copy commits. It's also how `hg fold`
behaves (though hg doesn't allow the description to be empty).
I was using a custom `jj log` command and had some trouble finding
the commit `jj merge` created. The default `jj log` command shows it
by default, but my custom one didn't.
The two commands are very similar and we should probably make one an
alias of the other (or just delete one), but for now let's at least
make them more similar by supporting `-m` for both.
I currently think `jj new` is more natural when starting a new change
on top of the current one and `jj checkout` is more natural when
starting a new change on top of another one, as well as when you just
want to look around or run tests. `jj checkout` doesn't currently
default to the working copy like `jj new` does. Perhaps we should make
it do that. Will people eventually feel that it's natural to run `jj
checkout` to create a new change on top of the working copy, or will
they feel that it's natural to run `jj new` on an unrelated commit
even to just look around, or will we want them as synonyms forever?
I was initially worried about the cost of always snapshotting the
working copy, so that's why e.g. `jj diff -r <some hash>` doesn't do
it. However, there's been a few caused by missing snapshotting, and
there are still a few (I just noticed it in `jj undo` while writing
this patch). Let's always do the snapshotting and if the user really
doesn't want it, they can pass `--no-commit-working-copy` (which we
should probably rename to `--no-snapshot-working-copy` or maybe just
`--no-snapshot`). That should reduce bugs and make the CLI more
predictable.
Two test cases were affected becasue `jj merge` also didn't snapshot
the working copy.
Before this patch, e.g. `jj co --no-commit-working-copy` would error
out, but now it will succeed (without touching the working copy,
leaving the working copy stale). That may be confusing, but it should
be easy to recover from (e.g. by `jj undo`). We can consider adding a
check for it later if it seems too confusing (it's probably rarely
something the user wanted).
Since we now allow pushing open commits, we can implement support for
pushing the "current" branch by defining a "current" branch as any
branch pointing to `@`. That definition of a current/active seems to
have been the consensus in discussion #411.
Closes#246.
When rebasing commits after rewrites, we also update all workspaces'
checkouts. If the new commit is closed, we create a new commit on
top. Since we're hoping to remove the open/closed concept, we need a
new condition. I considered creating a new commit on top if the change
ID was different from before the rewrite. However, that would make at
least `jj split` more complicated because it makes the first commit
keep the change ID but it wants the second commit to be checked
out. This patch instead creates the new commit on top only when the
original commit was abandoned.
This patch adds `jj obslog -p` for including the diff compared to the
predecessor (the first predecessor if there are several). If the
predecessor's parents are different, then we create a temporary tree
by rebasing the predecessor to have the same parents and we use the
result as base for the diff. That way, we avoid polluting the diff
with the changes caused by the rebase. (I don't think we currently
have any commands that can change both parents and content, so the
diff should always be empty for rewrites caused by a rebase.)
Working on this also reminded me that it'll be really nice when we
replace `jj obslog` by something based on the operation log - I really
miss seeing information about the operation in the output (like `hg
obslog` gets from its obsmarkers).
I often redirect the jj output to pager, so I set ui.color = "always" in
config file. This patch allows me to remove such config, and instead specify
--color=always only when needed.
According to the NO_COLOR FAQ, "user-level configuration files [...] should
override $NO_COLOR." https://no-color.org/
Unfortunately this makes it harder to test the $NO_COLOR behavior since the
test environment isn't attached to a tty. We could allocate a pty or
LD_PRELOAD shim to intercept isatty(), but I feel it would be too much to do.
https://github.com/assert-rs/assert_cmd/issues/138
This patch prevents perhaps pushing commits with an empty description
or the placeholder "(no user/email configured)" values for
author/committer.
Closes#322.
If a commit's author field has the placeholder user/email values
(i.e. "(no name configured)" and "(no email configured)"), and they
have now configured their email and username, they probably want us to
update the author field with the new information, so that's what this
patch does. Thanks to durin42@ for the suggestion on #322.
If the source commit becomes empty as a result of
`move/squash/unsquash`, we abandon it. However, perhaps we shouldn't
do that if the source commit is a working-copy commit because
working-copy commits are often work-in-progress commits.
The background for this change is that @arxanas had just started a new
change and had set a description on it, and then decided to make some
changes in the working copy that should be in the parent
commit. Running `jj squash` then abandoned the working-copy commit,
resuling in the description getting lost.
Before this change, `jj new` would check out the new commit only if it
was created on top of the current commit. I never liked that
special-casing, and after thinking more about how the open/closed
should work (see discussion #321), I think we want `jj new` to behave
similar to how `git/hg checkout` works, so it can effectively replace
the current `jj checkout` command for the use case of starting new
work on top of an existing commit.
This adds a `--reversed` flag to `jj log` to show commits with later
commits further down. It works both with and without the graph.
Since the graph-drawing code is already independent of the
relationship between commits, it doesn't need any updating.
The default log output of showing all commits is not very useful when
contributing to an existing repo. Let's have it default to showing
commits not on any remote branch instead. I think that's the best we
can do since we don't have a configurable main branch yet, and we
don't even have per-repo configuration..
Closes#250.
Our support for aliases is very naively implemented; it assumes the
alias is the first argument in argv. It therefore fails to resolve
aliases after global arguments such as `--at-op`.
This patch fixes that by modifying the command defintion to have an
"external subcommand" in the list of available commands. That makes
`clap` give us the remainder of the arguments when it runs into an
unknown command. The first in the list will then be an alias or simply
an unknown command. Thanks to @epage for the suggestion on in
clap-rs/clap#3672.
With the new structure, it was easy to handle recursive alias
definitions, so I added support for that too.
Closes#292.
With this patch, the order is this:
`$JJ_EDITOR` environement variable
`ui.editor` config
`$VISUAL` environement variable
`$EDITOR` environement variable
`pico`
That matches git, except that git falls back to an editor determined
at compile time (usually `vi`) instead of using `pico`.
As I said in 095fb9fef4, removing support for `~/.jjconfig` was an
experiment. I've heard from a few people (including in #233) that they
would prefer to have configs in the home directory. This patch
therefore restores that functionality, except I added a `.toml`
extension to the file to clarify the expected format to users and
editors.
After this patch, we still allow configs in `$XDG_CONFIG_HOME` (and
the other paths used by `dirs::config_dir()`), but we error out there
are config files in both that location and `~/.jjconfig.toml`.
Apparently, I need to pass `--merge` option to use kdiff3 as a diff editor.
We could add `diff-editor-args` or extend `diff-editor` to a list of command
arguments, but we'll eventually add stock merge tools and the configuration
would look like:
[merge-tools.<name>]
program = ...
diff-args = [...]
edit-args = [...]
merge-args = [...]
This adds `jj git push --change <revision>` which creates a branch
with a name based on the revision's change ID, and then pushes that
like with `--branch`. That can be useful so you don't have to manually
add the branch (and come up with a name for it). The created branch
behaves like any other branch, so it's possible to make it point to a
commit with a different change ID.
As requested by @talpr. I added this is a separate new command `jj git
remote list`. One could also imagine showing the listing when there is
no sub-command specified to `jj git remote`, but we don't have other
commands that behave that way yet.
Closes#243
Now that I'm using GitHub PRs instead of pushing directly to the main
branch, it's quite annoying to have to abandon the old commits after
GitHub rebases them. This patch makes it so we compare the remote's
previous heads to the new heads and abandons any commits that were
removed on the remote. As usual, that means that descendants get
rebased onto the closest remaining commit.
This is half of #241. The other half is to detect rewritten branches
and rebase on top.
This adds a `jj sparse` command with options to list and manage the
set of paths to include in the working copy. It only supports includes
(postive matches) for now.
I'm not sure "sparse" is the best name for the feature. Perhaps it
would make sense as a subcommand under `jj workspace` - maybe `jj
workspace track`? However, there's also `jj untrack` for removing a
file from the working copy and leaving it in the working copy. I'm
happy to hear suggestions, or we can get back to the naming later.
I originally made the operation argument a named argument
(`--operation`) to allow for a change ID to be passed as a positional
argument, matching e.g. `hg revert -r <rev> <path>`. However, even if
we add support for undoing changes only to certain change IDs, it's
going to be done much less frequently than full undo/restore. We can
therefore make that a named argument if we ever add it.
The `DescendantRebaser` keeps a map of branches from the source
commit, so it gets efficient lookup of branches to update when a
commit has been rebased. This map was not kept up to date as we
rebased. That could lead to branches getting left on hidden
intermediate commits. Specifically, if a commit with a branch was
rewritten by some command, and an ancestor of it was also rewritten,
then we'd only update the branch only the first step and not update it
again when rebasing onto the rewritten ancestor.
When a directory is missing in one merge input (base or one side), we
would consider that a merge conflict. This patch changes that so we
instead merge trees by treating the missing tree as empty.
This introduces a `connected(x)` function, which is simply the same as
`x:x`. It's occasionally useful if `x` is a long expression. It's also
useful as a building block for `root(x)` (coming soon).
It's annoying especially for tests to not be able to append to a
config file without knowing the contents (as you have to do with
TOML). Let's read all files in a directory if `$JJ_CONFIG` points to a
directory. Mercurial does that for its `$HGRCPATH` variable.
I quite often want to move the changes to a particular file from one
commit to another. We already support that using `jj move -i`, but
that can be annoying to run because we don't have a TUI for it
(#48). Let's make it possible to do `jj move --from X --to Y <path>`.
It seems very unlikely that the user would want to untrack all paths
(that's still possible with `jj untrack .`, if they really want to,
and have added all their current paths to the `.gitignore`).
I'm adding this mostly because it's useful for testing. That's also
the reason it supports displaying conflicts. I didn't call it `cat`
like `hg cat` because I haven't found `hg cat` on multiple files
useful.
This release is mostly about the fix for #177, which looks pretty bad
even though I think it is actually harmless. It also has `jj log -p`
contributed by @yuja!
When initializing a workspace that shares its working copy with a Git
repo (i.e. `jj init --git-repo=.`), we import refs and HEAD when
creating the `WorkspaceCommandHelper` (as we do for all commands when
the working copy is shared). That makes the explicit import we do in
`cmd_init()` unnecessary. It also makes the checkout of HEAD I added
for the fix of #102 unnecessary. More importantly, as @yuja reported
in #177, it makes the command crash (at least if the repo is small
enough that the two checkouts happen within a second). I think the
problem is that the second checkout tries to create the same commit
except that the Change ID is different (the problem is not the
predecessors as I speculated in the issue tracker). The fix is to
simply avoid doing the redundant work. We still need a proper fix for
#27 eventually.
Closes#177.
This patch adds a very simple e2e test of having a working copy shared
with Git. The test initially failed on Windows. The symptom was that
the "master" branch did not get updated when we create a commit using
`jj`. That suggested that we didn't correctly detect that the working
copy was shared. After a lot of troubleshooting, I think I mostly
understand what we going on here (thanks to @arxanas for suggesting
https://github.com/mxschmitt/action-tmate). The path we get from
`git2::Repository::workdir()` seems to not be canonicalized in the
same way as `std::fs::canonicalize()` canonicalizes. Specifically, it
does not have the "\\?\" prefix we get from that function. I suppose
that's because libgit2 is a C library and canonicalizes the path using
some other system call.
"log -p | less" is the option I often use with hg/git to find interesting
bits from the changelog, and I think it's also valid with jj. Unlike
"hg log -p --stat", "jj log -p --summary" does not show both diff summary
and patch to reflect the internal structure. This behavoir is arguable and
may be changed later.
The logic of show_patch() is extracted from cmd_show().
It's unusual for the current commit to have descendants, but it can
happen. In particular, it can easily happen when you run `jj new`. You
probably don't want to abandon it in those cases.
It's useful for tests, scripts, and debugging to be able to use
specific config instead of the user's config. That's especially true
for our automated tests because they didn't have a place to read
config from on Windows before this patch (they read their config from
`{FOLDERID_RoamingAppData}`, which I don't think we can override in
tests).
I'm a little hesitant to do this because most tools I'm familiar with
have the config file directly in `~/`. It's also easier to describe
where to put the file if it doesn't vary across platforms. But we're
still early in the project, so let's try it and see if we get any
complaints.
I thought that `std::fs::canonicalize()` expanded "~", but it doesn't
seem to do that, which caused #131. Git seems to do the expansion
itself, so we probably also should. More importantly
`std::fs::canonicalize()` crashes when the file doesn't exist. The
manual expansion we do now does not.
Closes#131.