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.
Most commands that are going to make changes to the working copy want
to share some logic for detecting concurrent changes, so let's extract
that to a function.
It's useful for testing to be able to specify some operation that's
not the latest one.
I didn't update the changelog because this feature is mostly for
testing.
When loading a repo, `@` means the latest operation, possibly even
merging any concurrent operations. After loading a repo, `@` means the
operation the repo was loaded at. For example, when running `jj
--at-op=abc123 undo -o @`, `@` will undo operation `abc123`. This
patch therefore makes `resolve_single_op()` more generic by letting
the caller pass in what `@` should resolve to. I also added version of
the function on `WorkspaceCommandHelper` for convenience.
The function doesn't make anything simpler for us, and I think it will
be easier to implement simple "opsets" (like "revsets" for operations)
if we work directly with the operation objects (instead of repo
objects).
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.
Somehow I had thought that setting `clap(required = true)` on a
list-type argument was not enough to convince clap to require at least
one occurrence, but it seems that it does. The `min_values = 1` I had
added to get it to work actually means that we accept multiple values
passed to a single argument (e.g. `jj rebase -d x y z`), which is not
what I had intended.
This addresses a TODO I had left in the code. In addition to the
reasons I mentioned in the TODO, config sources are also better
because they can be layered. I'm planning on using that for editor
configs, letting `$EDITOR` be a layer under the configs and
`$JJ_EDITOR` be a layer over (matching how `git` does it).
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 removes the ui dependency from show_diff(), and clarifies that the
formatter argument is the output stream.
We might eventually trun DiffFormat into a struct, but it's an enum for
now.
With this change, we can eliminate (some of) the ui argument from diff
functions.
parse_file_path() can also be moved to WorkspaceCommandHelper, but I'm
yet to be sure how to reorganize it and matcher builder.
This prepares for the removal of the ui argument from diff functions. I
think it's a bit confusing that these functions take (ui, formatter)
parameters since both ui/formatter can be output stream.
Ui::format_file_path() will be moved to WorkspaceCommandHelper.
show_git_diff() doesn't need WorkspaceCommandHelper as of now, but I've
also changed its signature for consistency. If we add an option to specify
path formatting of git diff for example, we'll probably need the command
helper.
I've found it hard to read the `jj help` output because command
options are mixed with global options. This patch fixes that by
putting global options under a separate heading.
We do it for all the other kinds of objects already. It's useful to
have the path for backends that store objects by path (we don't have
any such backends yet). I think the reason I didn't do it from the
beginning was because we had separate `RepoPath` types for files and
directories back then.
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().
We might want to split show_diff() into config/option handling part and
diff displayer function, but I'm not sure. Since some of the show_diff
functions depends on ui, we can't isolate show_diff() from the ui object
anyway.
let opts = parse_diff_option(ui, args); // map config/option to diff opts
show_diff(ui, formatter, opts, ...); // would be nice if ui could be removed
This parepares for "log --patch" option, where the formatter will be passed
as a function argument. Unlike diff/show, graphlog needs a temporary output
buffer per commit.
Successful commands should probably not write to stderr. If we later
add commands that e.g. print warnings to stderr, we'll want to cover
that explicitly in test.
This involved copying `UnresolvedHeadRepo::resolve()` into the CLI
crate (and modifying it a bit to print number of rebased commit),
which is unfortunate.
Certain commands should never rewrite commits, or they take care of
rebasing descendants themselves. We have an optimization in
`commands.rs` for those commands, so they skip the usual automatic
rebasing before committing the transaction. That's risky to have to
remember and `MutableRepo` already knows if any commits have been
rewritten (that wasn't the case before, in the Evolution-based
code). So let's just have `MutableRepo` do the check instead.
We very often expect success, and we sometimes want to get the stdout,
too. Let's add a convenience function for that. It saves a lot of
lines of code.
I had accidentally given the two positional arguments for `jj git
remote add` the same index. While fixing that, I realized that maybe
`clap` can infer the index based on the declaration order in the
struct. That does indeed seem to work, so I just removed all the
explicit `index` arguments instead.
The Derive API is easier to work with, less error-prone, and less
verbose, so it seems like a good improvement.
It was quite a bit of work to make the switch, and I'll be surprised
if I didn't make any mistakes in the translation. We unfortunately
don't have enough e2e tests to be very confident, so we'll have to fix
any problems as we discover them.
I've at least verified that the output of `jj debug completion --fish`
is unchanged, except for a few help texts I changed for different
reasons (consistency, clarity, avoiding redundancy).
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.
It probably doesn't make sense to respect Git's `core.excludesFile`
config when not running in a Git-backed repo, but we also already
respect `.gitignore` files in the working copy regardless of backend,
so at least it's consistent with that. We can revisit it when the
native backend becomes a reasonable choice.
Closes#87.
The library crate shouldn't look up the user's `$HOME` directory
(maybe the library is used by a server process), so let's have the
caller pass it into the library crate instead.
I'm about to make callers pass in "base" Git ignores when writing a
tree from the working copy. `edit_diff()` will then need to pass
that. It'll be easier to do that if we have a proxy on
`WorkspaceCommandHelper`.
Open commits are work-in-progress and `jj git push --branch <name>`
therefore errors out if the branch points to an open commit. However,
we don't do the same check if you run `jj git push` to push all
branches. This patch introduces such a check. Rather than error out,
we skip such branches instead.
I didn't make it check for open commits in ancestors. It's quite
unusual (at least in my workflow) to have a closed commit on top of an
open one. We can revisit if users get surprised by it.
It rarely makes sense to push commits with conflicts to a remote Git
repo (which is the only kind of remote we support so far), so let's
just error out instead of pushing a commit that others pulling from
the remote probably can't make sense of.
I've only added a simple test for the error case for now. `libgit2`
doesn't support pushing to a local repo, so it's harder to test the
success case. I suppose we'll have to have the regular `git` binary
running local servers in test eventually.
Closes#60.
We don't want variables from the developer's environment to affect
tests. You can make tests fail if you set `XDG_CONFIG_HOME` such that
`$XDG_CONFIG_HOME/jj/config.toml` exists and has e.g. an email
set. The fix is to not propagate any environment variables. Thanks for
@arxanas for pointing this out in #104.
We still need to set `$HOME` to prevent configs from being read from
the process owner's home directory (because that's what
`dirs::config_dir()` seems to fall back to if `$HOME` is not set). By
the way, I suspect we'd still not immune to configs from the
developers home directory on Windows, because that doesn't seem to be
controlled by `$HOME`.
When the backing Git repo is inside the workspace (typically directly
in `.git/`), let's point to it by a relative path so the whole
workspace can be moved without breaking the link.
Closes#72.
When using an internal Git repo (`jj init --git`), we make
`.jj/repo/store/git_target` point directly to the repo (which is bare
in that case). It makes sense to do the same when using an external
Git repo (`jj init --git-repo`), so the contents of
`.jj/repo/store/git_target` doesn't depend on whether the user
included the `.git/` on the CLI.
This patch introduces a `JJ_TIMESTAMP` environment variable that lets
us specify the timestamp to use in tests. It also updates the tests to
use it, which means we get to simplify the tests a lot now that that
the hashes are predictable.
We need the app (top-level `clap::Command`) in order to check if
e.g. `-R` was passed to `jj init` (for #101), and it seems cleaner to
pass the instance around than to re-create it when needed.
As pointed out by @arxanas in #88, the message saying something like
"At least 'bin/.DS_Store' was added back ..." is confusing especially
when the command you ran was just `jj untrack bin/.DS_Store`. Let's
clarify the message by saying exactly how many more files there are,
and specialize the message for when there is only one file. Also
update the message to say "would be added back" instead of "was added
back" since we don't actually change anything if some files would be
added back (since 4b91ad408c).
Should we even list all the files? I'm concerned that such a list
could be very long. On the other hand, it can also be annoying to have
to run `jj untrack some/dir/` and only be told about single file to
add to the ignore patterns every time.
When I recently changed the working copy to not have a commit ID
(e098c01935), I lost the check in `update_working_copy()` in
`commands.rs` that made us not print "Working copy now at: " if the
commit was unchanged. Now we always print, which is unnecessary and
confusing (it makes it seem like the commit changed even if it
didn't). Let's restore the check.
It can be useful in command prompts and scripts to be able to quickly
get e.g. the `jj status` output without spending time committing the
working copy (perhaps because some background process continuously
commits the working copy). One can already do that by passing
`--at-op=<operation ID>`, but then one needs to look up the operation
ID first. That is both extra work for the user/script and it means
there's an extra `jj op log` invocation to get the operation ID. Let's
have a global flag to make it easy and efficient to do.
When running in a working copy collocated with git's, we export the
working copy's commit's parent to git after every command. However, we
forgot to update our own record of git's HEAD. That means that on
subsequent imports from git, it'll look like the user had updated HEAD
using a git command. When we detect that, we trust that the user had
taken care of the changes in the working copy and we simply abandon
our old working copy commit. That led to the bug reported in $54,
where the second commit of a `jj split` got lost.
The fix is to also update our record of where git's HEAD is when we
tell git to update it.
Closes#54.
We no longer need the commit ID, so we shouldn't make the callers pass
it. This lets us simplify several tests, because they no longer to
create commits just to check out a tree in the working copy.
We used to use the value to detect races, but we use the tree ID and
the operation ID these days, so we don't need the commit ID.
By changing this, we can avoid creating some commit IDs in tests,
which is why I tackled this issue now.
There are only two callers of `LockedWorkingCopy::check_out()`. One is
in `commands.rs`. That caller already checks after taking the lock
that the old commit ID is as expected. The other caller is
`WorkingCopy::check_out()`. We can simply move the check to that level
since it's the only caller that cares now.
A few commands (`restore`, `diff`, and `untrack` so far) accept path
arguments, but they only support files. Let's make them work with
directories too.
It's harmless but potentially confusing to have multiple workspaces
with the same ID (it would mean that they always have the same
checkout). Let's just prevent it for now. We can add an override later
if people think of usecases for it.
When you run `jj co abc123` and that commit is already checked out, we
just print a message. The condition for that assumed that the checkout
existed, which it won't if you just ran `jj workspace forget`. Let's
avoid that crash, especially since `jj co` is an easy way to restore
the working copy if you had accidentally run `jj workspace forget`
(though `jj undo` is even easier).
It seems helpful to show in the log output which commit is checked out
in which workspace, so let's try that. I made it only show the
information if there are multiple checkouts for now.
In workspaces added after the initial one, the idea is to have
`.jj/repo` be a file whose contents is a path to the location of the
repo directory in some other workspace.
Because we record each workspace's checkout in the repo view, we can
-- unlike other VCSs -- let the user refer to any workspace's checkout
in revsets. This patch adds syntax for that, so you can show the
contents of the checkout in workspace "foo" with `jj show foo@`. That
won't automatically commit that workspace's working copy, however.
If the workspace is shared with a Git repo, we sometimes update Git's
HEAD ref. We should get the new checkout from the right workspace ID
when doing that (though I'm not sure we'll ever support sharing the
working copy with Git in a non-default workspace).
When importing Git HEAD, we already use the right workspace ID for the
new checkout, but the old checkout we abandon is always the default
workspace's. We should fix that even if we will never support sharing
a working copy with Git in a non-default workspace.
Before committing the working copy, we check if the working copy is
checked out to the commit we expect based on the repo's view. We
always use the default workspace's checkout, so we need to fix that.
We detect concurrent working copy changes by checking that the old
commit matches the repo's view. We should use the current workspace
when looking up the checkout in the view.
This adds a `jj move [--from <rev>] [--to <rev>] [-i]` command, which
lets you move some changes from one commit into another. `jj
squash/amend` is just a special case of this new command. Except for
that command's more specialized help text, instructions, etc., it
could be implemented as simply `jj move --to @-`.
I thought it was a bit unclear which part of the process was
interactive (it's only choosing parts of the diffs that is
interactive, not choosing destination or anything else).
We allow rebasing to a descendant, but that causes divergence because
the old commit remains visible. You could imagine making it work so
`jj rebase -r B -d D` on a linear chain "A-B-C-D" reorders it to
"A-C-D-B", but we don't do that yet, so let's just prevent the
divergence for now.
Now that we have the operation ID recorded in the working copy state,
we can tell if the working copy is stale. When it is, we update it to
the repo view's checkout.
When there are concurrent operations that want to update the working
copy, it's useful to know which operation was the last to successfully
update the working copy. That can help use decide how to resolve a
mismatch between the repo view's record and the working copy's
record. If we detect such a difference, we can look at the working
copy's operation ID to see if it was updated by an operation before or
after we loaded the repo.
If the working copy's record says that it was updated at operation A
and we have loaded the repo at operation B (after A), we know that the
working copy is stale, so we can automatically update it (or tell the
user to run some command to update it if we think that's more
user-friendly).
Conversely, if we have loaded the repo at operation A and the working
copy's record says that it was updated at operation B, we know that
there was some concurrent operation that updated it. We can then
decide to print a warning telling the user that we skipped updating
because of the conflict. We already have logic for not updating the
working copy if the repo is loaded at an earlier operation, but maybe
we can drop that if we record the operation in the working copy (as
this patch does).
When importing git HEAD in a working copy shared with git, we reset
the working copy to the new commit at the end. If we fail to reset the
working copy, we shouldn't commit the operation. This patch mostly
fixes that by locking the working copy while we commit the
operation. There's still a small risk that the operation commits and
we fail to write the working copy state, but there's not much we can
do about that (or it's not worth the effort anyway).
`WorkingCopy::check_out()` currently fails if the commit recorded on
disk has changed since it was last read. It fails with a "concurrent
checkout" error. That usually works well in practice, but one can
imagine cases where it's not correct. For an example where the current
behavior is wrong, consider this sequence of events:
1. Process A loads the repo and working copy.
2. Process B loads the repo at operation A. It has not loaded the
working copy yet.
3. Process A writes an operation and updates the working copy.
4. Process B loads the working copy and sees that it is checked out
to the commit process B set it to. We don't currently have any
checks that the working copy commit matches the view's checkout
(though I plan to add that).
5. Process B finishes its operation (which is now divergent with the
operation written by process A). It updates the working copy to
the checkout set in the repo view by process B. There's no data
loss here, but the behavior is surprising because we would usually
tell the user that we detected a concurrent update to the working
copy.
We should instead check that the working copy's commit on disk matches
what the previous repo view said, i.e. the view at the start of the
operation we just committed. This patch does that by having the caller
pass in the expected old commit ID.
I was surprised that we save the `TreeState` before
`LockedWorkingCopy::finish()`. That means that even if the caller
instead decides to discard the changes, some changes will already have
been written.
This patch changes the interface for making changes to the working
copy by replacing `write_tree()` and `untrack()` by a single
`start_mutation()` method. The two functions now live on the returned
`LockedWorkingCopy` object instead. That is more flexible because the
caller can make multiple changes while the working copy is locked. It
also helps us reduce the risk of buggy callers that read the commit ID
before taking the lock, because we can now make it accessible only on
`LockedWorkingCopy`.
`WorkingCopy::current_commit()` has been there from the beginning. It
has made less sense since we made the repo view keep track of the
current checkout. Let's remove it.
Before this patch, we got the old commit ID before we took the lock on
the working copy, which means we might unnecessarily create divergence
if another process just committed the working copy.
It turns out that the `--help` option is "global", so the description
we set on the top-level command already applies to subcommands (and
subsubcommands, etc.).
I wanted to have all the documentation available on the command line,
but that makes it harder to maintain and link to. Let's move it to
markdown instead. We may later be able to add some way of presenting
the markdown in the terminal (or maybe by first converting it to
reStructuredText).
Now that it's much easier to use a shared working copy between git and
jj, let's update the hint about how to set up a jj repo backed by the
git repo to use a shared working copy.
When initializing a jj repo in the same directory as its backing git
repo, add `.jj/` to `.git/info/exclude` so it doesn't show up to `git`
commands.
This is part of #44.
If the workspace's working copy is shared with the backing Git repo,
we now automatically update the checkout in jj to match Git's HEAD
when that has changed.
With this change, I think users should be able to run `jj init
--git-store=.` and then continue to use `git` commands and
non-mutating `jj` commands without issue.
This is part of issue #44.
This change makes commands automatically import git refs if they're
run in a workspace that shares its working copy with the underlying
git repo. The import is done in a separate transaction.
This is part of #44.
It's useful to know which commit is checked out in the underlying Git
repo (if there is one), so let's show that. This patch indicates that
commit with `HEAD@git` in the log output. It's probably not very
useful when the Git repo is "internal" (i.e. stored inside `.jj/`),
because then it's unlikely to change often. I therefore considered not
showing it when the Git repo is internal. However, it turned out that
`HEAD` points to a non-existent branch in the repo I use, so it won't
get imported anyway (by the function added in the previous patch). We
can always review this decision later.
This is part of #44.
If nothing changed in a transaction, it's rarely useful to commit it,
so let's avoid that. For example, if you run `jj git import` without
changing the anything in the Git repo, we now just print "Nothing
changed.".
Some time ago, I made commands not commit the working copy when run at
an old operation, but it seems that I forgot to make it not update the
working copy. If you run e.g. `jj --at-op=<some operation> rebase -d
<some commit>`, it doesn't make sense for that to update the working
copy.
`ReadonlyRepo::init_*()` currently calls `WorkingCopy::init()`. In
order to remove that dependency, this patch wraps the
`ReadonlyRepo::init_*()` functions in new `Workspace` functions. A
later patch will have those functions call `WorkspaceCopy::init()`.`
The `Repo` doesn't do anything with the `WorkingCopy` except keeping a
reference to it for its users to use. In fact, the entire lib crate
doesn't do antyhing with the `WorkingCopy`. It therefore seems simpler
to have the users of the crate manage the `WorkingCopy` instance. This
patch does that by letting `Workspace` own it. By not keeping an
instance in `Repo`, which is `Sync`, we can also drop the
`Arc<Mutex<>>` wrapping.
I left `Repo::working_copy()` for convenience for now, but now it
creates a new instance every time. It's only used in tests.
This further decoupling should help us add support for multiple
working copies (#13).
Having a concept of a "workspace" will be useful for adding support
for multiple workspaces (#13). You can think of the "workspace" as a
repo combined with a working copy. A workspace corresponds 1:1 with a
`.jj/` directory. It's pretty close to what other VCS simply call a
"repo", but I've ended up using the word "repo" for what Git calls a
"bare repo".
I was confused myself why the message was only printed by `jj co` and
not e.g. `jj undo`. That probably means that it should always be
printed (or never be printed).
Especially when working on a new-to-you project, it's common to end up
with unwanted files automatically tracked before you realize that you
should have added them to the `.gitignore`. Even after adding them to
the `.gitignore`, it's not trivial to make them no longer tracked (you
need to move them away, run e.g. `jj st`, then move them back). This
patch adds `jj untrack` to simplify that (without actually moving the
files).
Closes#14.
The diff-editing code shares the `TreeState` functionality with the
working-copy code. That means we can now let the user edit conflicts
without materializing them first. So now the user can do e.g. `jj edit
-r <some commit>` and resolve only some of the conflicts.
As @arxanas noted, it's hard to tell which commit is currently checked
out. Hopefully bright colors will help. Maybe setting a background
color would be even clearer, but that's harder to do because the
formatter doesn't support background colors yet.
The CLI code for cloning a Git repo didn't use the usual
`finish_transaction()` method, because we didn't have support for
doing that on a repo that was creating half-way through a
command. That led to a bug where it would leave the initial checkout
(the one on top of the root commit) after checking out the correct
head.
`args` seems to make it clear that these are command-line arguments
(this is not Python, so there's no `*args`). It also avoids the risk
of conflicts and confusion with other matches (e.g. file patterns or
regexes).
Diffs between certain combinations of file types were not handled by
`jj diff` (for example, a diff between a conflict and another conflict
would not show a diff). This change fixes that, and also makes added
and removed files get printed with color and line numbers, which I've
often wanted.
The user currently has to edit `.jj/git/config` (or run `git
--git-dir=.jj/git config`) to manage remotes in the underlying Git
repo. That's not very discoverable (and we may change the path some
day), so let's provide a command for it.
As #33 says, the default diff we have can be hard to read and it
cannot be used for use with other tools. This patch adds a `jj diff
--git` mode for showing Git's flavor of unified diffs.
We should add a config option to get these diffs by default. For
interchange with other tools, we also need a way of turnning off color
codes in output (it's currently always on, even when when not printing
to a TTY).
It's been a lot of work, but now we're finally able to remove the
`Evolution` state! `jj obslog` still works as before (it just walks
the predecessor pointers).
This rewrites the `divergent` template keyword to be based on the
number of visible commits with a given change id. That's the same as
before; it's just that it's not based on the `Evolution` object's view
of which commits are visible anymore.
This is the last thing that depended on the evolution state!
Now that we no longer have to be careful whether we mean "all heads"
or "non-obsolete heads", there's no need to pass them as
arguments. It's still possible to get a DAG range to a hidden commit
by using `RevsetExpression::dag_range_to()`, as long as the hidden
commit is indexed.
Now that we remove hidden heads whenever a transaction commits,
`non_obsolete_heads()` should always be the same as `all_heads()`,
except during a transaction. I don't think we depend on the difference
even during a transaction. Let's simplify a bit by removing the revset
function `all_heads()` and renaming `non_obsolete_heads()` to
`heads()`. This is part of issue #32.
Branches and the working copy are currently updated to the parents of
the old commit (if they pointed to the old commit to start
with). That's not what's supposed to happen; they're supposed to be
updated to point to the new commit. This patch fixes that by manually
rebasing the immediate children of the old commit, so that
`MutRepo::create_descendant_rebaser()` will do the right thing.
This patch removes the `jj evolve` command, as part of removing the
evolution feature (#32). The command is very rarely useful since we
started evolving orphans after every transaction. I haven't used `jj
evolve` in many months.
After removing the command, the only good way of resolving divergence
will be `jj abandon` one side of the divergence. We will no longer
have a way of automatically merging the divergent commits. I plan to
add back that functionality, powered by the operation log instead of
the obsolescence log. That will probably take a long time, but it's
not a very high-priority feature anyway, so I think it's okay to lose
it for a while.
This is similar to how a recent change taught `DescendantRebaser` to
update branches pointing to rewritten commits. Now we also update the
checkout if it pointed to a rewritten commit.
This patch moves the logic for updating branches from
`update_branches_after_rewrite()` into `DescendantRebaser`. The
branches are now updated along with each rebased commit rather than
all being updated at the end. The new code uses the information about
rewritten and abandoned commits that `DescendantRebaser` gets from
`MutableRepo`. That is different from the old code, which used the
evolution state. This patch thus moves us one step closer to removing
evolution (#32).
I'm going to teach `DescendantRebaser` to also update local branches
pointing to rewritten commits, taking over the responsibility from
`rewrite::update_branches_after_rewrite()`. For commits that have been
rewritten as multiple new commits (divergent, not split), that
function makes local branches pointing to the old commit point to all
the new commits. To replicate that behavior in `DescendantRebaser`, it
needs to know about divergent changes. This change addresses that.
I recently made the CLI remove hidden heads when a transaction is
committed (38474a9). Let's move that to `Transaction::commit()`, so
the library crate becomes more similar to how the CLI behaves and more
similar to our evolution-less future (#32).
This makes `jj rebase` and `jj abandon` rebase descendants using
`MutableRepo::create_descendant_rebaser()`, except that `jj rebase -r`
needs to be special-case since it doesn't rebase descendants onto the
rewritten commit.
This change makes it so we rebase descendants based on the rewrite
information recorded in `MutableRepo` instead of using evolution for
it. This is an important step towards removing evolution (#32).
When we remove evolution (#32), I don't intend to replicate the hack
it had for rebasing descendants onto a split commit. Let's instead
have `jj split` manually rebase descendants of the original commit
onto the second part of the split. We use `DescendantRebaser` for
that. Branches and the working copy pointing to the split commit are
still updated using evolution in
`RepoCommandHelper::finish_transaction()`. I plan to have
`DescendantRebaser` update branches and working copies as well. That
should then also work as expected for `jj split`.
This change makes `jj squash` and `jj unsquash` record the child or
parent (respectively) as abandoned if it becomes empty. We need to do
that because it won't get automatically recorded by
`CommitBuilder`. We could make `CommitBuilder` record abandoned
commits when `set_pruned()` was called, but that would be
short-sighted since we're about to delete that function as part of
removing support for evolution (#32).
After writing a new working copy commit, we update branches pointing
to it, and we rebase descendants on top. However, we should be doing
that in the opposite order, so the branch updates apply to rewritten
descendants as well.
This makes it so that almost every command will remove non-visible
heads. That shouldn't have much visible effect, except that the
`all_heads()` revset will become the same as
`non_obsolete_heads()`.
It will help us remove support for evolution by cleaning up existing
repos. Existing repos (like mine) will lose the unwanted heads (~8.5k
heads in my case), so they don't clutter the repo once evolution is
gone.
This is part of issue #32.
This change makes it so `jj abandon` uses `DescendantRebaser` for
rebasing descendants of the abandoned commits. That makes it not
depend on evolution for it.
Descendants of abandoned commits should be rebased onto their parents,
or the rewritten parents if they had been rewritten. This patch
teaches `DescendantRebaser` to do that. It updates `jj rebase -r` to
use the functionality. I plan to also use it in `jj abandon`
(naturally, given the name), and for rebasing descendants of deleted
refs imported from `jj git refresh/fetch/push`.
The command's help text says "Abandon a revision", which I think is a
good indication that the command's name should be `abandon`. This
patch renames the command and other user-facing occurrences of the
word. The remaining occurrences should be removed when I remove
support for evolution.
This makes conflicted branches pointing to the working copy get
updated when the working copy changes, just like they are when it
changes for other reason (such as `jj describe`).
This patch moves the function for updating branches after rewrite from
`commands.rs` into `rewrite.rs`.
It also changes the function to update branches even if they were
conflicted or become conflicted. I think that seems better than
leaving branches on old commits. For example, let's say you have start
with this:
```
C main
|
B origin@main
|
A
```
You now pull from origin, which has updated the main branch from B to
B'. We apply that change to both the remote branch and the local
branch, which results in a conflict in the local branch:
```
C main?
|
B B' main? origin@main
|/
A
```
If you now rewrite C to C', the conflicted main branch will still
point to C, which is just weird. This patch changes that so the
conflicted side of main gets repointed to C'.
I also refactored the code to reuse our existing
`MutableRepo::merge_single_ref()`, which improves the behavior in
several cases, such as the conflict-resolution case in the last test
case.
I don't know why I hadn't already updated these. Maybe I thought it
wasn't necessary. That's probably true right now, but I want to make
`jj git fetch` and `jj git refresh` automatically rebase commits when
branches were updated on a remote or in the underlying Git repo. We
want to make sure that the working copy also gets updated then.
I forgot to remove the `fetch_head` from a variable name when I
recently realized that Git's `FETCH_HEAD` is not what we want to check
out after fetching.
It turns out that `FETCH_HEAD` is not the remote's `HEAD` (it's
actually not even a normal symbolic ref; it contains many lines of
commits and names). We're supposed to ask the remote for its default
branch instead. That's what this patch does.
It's annoying to have to add `--branch main` every time I push to
GitHub.
Maybe we should make it push only the current branch by default, but
we don't even have a concept of a current branch yet...
I'm not sure what the interface should be, but until public heads are
automatically removed when they disappear from a remote, this will at
least be useful as a workaround.
For example:
```
main: 4f2efc5bb873 cli: make `jj branches` say how much remote branches are ahead/behind
@origin (behind by 2 commits): 5023d8d360 Merge pull request #26 from martinvonz/git-comparison
```
It turns out that `--help` provides a longer version of the help text
than `-h` does. I only discovered that because I was wondering what
the difference between `clap::App::about()` and
`clap::App::long_about()` was. There's clap-rs/clap#1015 for tracking
it in clap, but let's clarify it ourselves for now by changing the
help text for `-h/--help`.
With this commit, you can run `jj concepts branches` to get help about
the "branches" concept. We don't have much help for other commands and
their arguments yet, but I'm starting with concept guides so we can
point to them as we add help for commands and their arguments.
I initially tried to make the command to get help be `jj help
--concept branches`. That would require replacing clap's
implementation of the help command with our own. clap-rs/clap#1350
prevented me from doing that. But I'm pretty happy with having it
under `jj concepts` anyway. It's probably more discoverable that way.
I tried to mimic clap's styling with yellow headings.
Clap uses present tense by default (e.g. "Prints help information"). I
considered switching our message to that style, but I found it harder
to describe some flags that way.
I think it makes sense to have a version of rebase that rebases the
descendants of the rebased commit onto the parents of the rebased
commit. Let's make `jj rebase -r` do just that. Let's also add `jj
rebase -s` (matching Mercurial's `hg rebase -s`) for rebasing a commit
and its descendants onto another commit.
Since both flavors of the command now explicitly rebase the
descendants (just to different destinations), I also made the command
not evolve orphans afterwards. That would have made sense regardless
of this commit.
This change makes `jj status` include a section about conflicted local
branches and another section about conflicted remote branches. They
show up only if there are conflicts. They include hints about how to
resolve.
The auto-rebasing of descendants doesn't work if you have an open
commit checked out, which means that you may still end up with orphans
in that case (though that's usually a short-lived problem since they
get rebased when you close the commit). I'm also about to make
branches update to successors, but that also doesn't work when the
branch is on a working copy commit that gets rewritten. To fix this
problem, I've decided to let the caller of `WorkingCopy::commit()`
responsible for the transaction.
I expect that some of the code that this change moves from the lib
crate to the cli crate will later move back into the lib crate in some
form.
I want to reuse this bit of code for evolving descendants of a
rewritten working copy commit.
I expect this to change again soon (I'll probably make it do a regular
rebase instead of evolve), but this will do for now.
This makes it so (local) branches get updated when the commit they
point to gets rewritten. If the branch was conflicted, we just print a
warning and don't update the branch (though one could imagine
rewriting the conflict). We also just print a warning if the new
target is unclear because the commit was rewritten into multiple new
commits (divergent).
The updating doesn't work when the working copy commit gets rewritten
because the working copy changed on disk. That's because that's done
in a separate transaction inside `working_copy.rs`. That's similar to
how orphans of the working copy commit don't get automatically
evolved. I'll fix both problems soon.
With this change, we no longer fail if the user moves a branch
sideways or backwards and then push.
The push should ideally only succeed if the remote branch is where we
thought it was (like `git push --force-with-lease`), but that requires
rust-lang/git2-rs#733 to be fixed first.
Now that we have native branches, we can make `jj git push` only be
about pushing a branch to a remote branch with the same name.
We may want to add back support for the more advanced case of pushing
an arbitrary commit to an arbitrary branch later, but let's get the
common case simplified first.
I had forgotten to make the `delete` argument a flag by giving it a
name, so instead it conflicted with `name` argument, as tests
discovered.
While at it, I also made `name` required. It wasn't before because I
originally had a single command for `jj branch` and `jj branches` and
then I didn't think to make it required when I split them up.
Now that our own branches and tags are updated when git refs are
updated and the user can use them to specify revisions, we can start
displaying them instead of the git refs. This commit adds new
`branches` and `tags` template keywords and updates the default
templates to use them instead of `git_refs`.
This adds support for having conflicting git refs in the view, but we
never create conflicts yet. The `git_refs()` revset includes all "add"
sides of any conflicts. Similarly `origin/main` (for example) resolves
to all "adds" if it's conflicted (meaning that `jj co origin/main` and
many other commands will error out if `origin/main` is
conflicted). The `git_refs` template renders the reference for all
"adds" and adds a "?" as suffix for conflicted refs.
The reason I'm adding this now is not because it's high priority on
its own (it's likely extremely uncommon to run two concurrent `jj git
refresh` and *also* update refs in the underlying git repo at the same
time) but because it's a building block for the branch support I've
planned (issue #21).
The new `diff::DiffHunk` type is very similar but more generic. We
don't need the generality here. I just don't two very similar types
with the same name.
This change teaches `Tree::diff()` to filter by a matcher. It only
filters the result so far; it does not restrict the tree walk to what
`Matcher::visit()` says is necessary yet. It also doesn't teach the
CLI to create a matcher and pass it in.
This patch makes it so we use color in the graph iff we use it other
output. We currently always use color except for in the smoke tests,
so it has no effect in practice. It's easy to turn off color when
stdout is redirected (using the `atty` crate), but I haven't done that
because I occasionally pipe `jj log` output to `less` and I want color
then.
This both helps find the current checkout and head operation and
hopefully helps teach the user that "@" is the symbol for the working
copy. I removed the current "<--" indication from the graph (and
non-graph) log template. Hopefully the "@" is clear enough on its own,
but we may want to add back some further indication later. We'll see.
I considered even changing the message to "Checking out: <commit>" as
that's technically more correct (the message is printed when the
view's checkout is updated, i.e. before the working copy is
updated). However, I worried that users would find it confusing that
e.g. `jj close` would result in a "Checking out: " message, even
though that's what actually happens.
I remember adding that message a long time ago so the user has a trace
of working copy commit ids in the terminal output. They should be able
to get the same information from the operation log combined with
e.g. `jj st --at-op`.
We already support using "@" to refer to the head operation when doing
e.g. `jj op undo -o @`. This patch adds support for `--at-op=@`. It
also makes that the default.
This prepares `jj status` for working better on an old repo state
(with `--at-op`). When looking at an old repo state, the "working
copy" should reflect the state from that state, i.e. the view's
"checkout", not the current working copy.
Before this patch, `jj log` would always commit the working copy and
most other commands would commit the working copy only if they were
passed a revset of exactly "@". This patch makes it so they all commit
the working copy unless they are passed just a symbol other than "@"
(typically a commit id). That means that we will not commit the
working copy if the user does `jj diff -r abc123`, but we will if they
do `jj diff -r :abc123`. It's clearly unnecessary in both those cases,
and we should fix, but this is probably good enough for now.
This patch adds checks in all (?) commands that rewrite commits to
make sure the commit they're about to rewrite is allowed to be
rewritten. The only check we do is that it's not a root commit. We
should at least add checks for public commits later.
Now that we auto-evolve after most operations, the user may not know
what "evolve" means. Even before that, the way `jj evolve` resolved
orphans after pruning was by rebasing them.
Perhaps it makes more sense to display the working copy commit just
above the changes in the working copy commit, even though that means
that the order between the working copy commit and the parent becomes
the opposite of the order in `jj log`.
I had initially hoped that the type-safety provided by the separate
`FileRepoPath` and `DirRepoPath` types would help prevent bugs. I'm
not sure if it has prevented any bugs so far. It has turned out that
there are more cases than I had hoped where it's unknown whether a
path is for a directory or a file. One such example is for the path of
a conflict. Since it can be conflict between a directory and a file,
it doesn't make sense to use either. Instead we end up with quite a
bit of conversion between the types. I feel like they are not worth
the extra complexity. This patch therefore starts simplifying it by
replacing uses of `FileRepoPath` by `RepoPath`. `DirRepoPath` is a
little more complicated because its string form ends with a '/'. I'll
address that in separate patches.
I thought I had looked for this case and cleaned up all the places
when I made `Transaction::commit()` return a new `ReadonlyRepo`. I
must have forgotten to do that, because there we tons of places to
clean up left.