ok/jj
1
0
Fork 0
forked from mirrors/jj
Commit graph

2211 commits

Author SHA1 Message Date
Martin von Zweigbergk
6ca7b5d352 repo: levarage the new static <backend type>::name() functions 2023-10-14 15:21:53 -07:00
Martin von Zweigbergk
f8be0b2030 backends: deduplicate definition of backend names
I copied the example set by `DefaultSubmoduleStore`.
2023-10-14 06:38:35 -07:00
Yuya Nishihara
9186d0fd38 workspace: convert external git repo path to store-relative by constructor
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
2023-10-14 22:20:09 +09:00
Yuya Nishihara
b7c7b19eb8 view: migrate in-memory structure to per-remote branches map
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.
2023-10-14 22:20:00 +09:00
Yuya Nishihara
3ec3cac90b revset: use op_store::View type to resolve branches()/remote_branches()
These functions depend heavily on the underlying data structure, and I haven't
decided abstract View API to access to per-remote data types. Let's use the
underlying data type for now.
2023-10-14 22:20:00 +09:00
Yuya Nishihara
0160eaefd9 view: add function that converts branches to/from per-remote view-like data 2023-10-14 22:20:00 +09:00
Yuya Nishihara
2b78275e22 view: add per-remote view types and iterator that reconstructs BranchTarget
I'm planning to add support for untracked remote branches, and under that
model, there will be many remote branches without local counterparts. That's
the main reason why remote branches are grouped by remote, not by branch name.

The added helper functions will be used by simple_op_store and view.
2023-10-14 22:20:00 +09:00
Yuya Nishihara
198dfa5cbe view: extract remove_remote() from git module
This will be just self.data.remote_views.remove(remote_name), so I'm not gonna
refactor the implementation at this point.
2023-10-13 18:12:45 +09:00
Yuya Nishihara
1d3c830e85 view: rewrite get_branch() callers in tests, remove the method
get_branch() would need to reconstruct the remote_targets map if we migrate
the underlying data structure to per-remote views. Let's remove the method as
it is only used in tests.
2023-10-13 18:12:45 +09:00
Yuya Nishihara
3fb0a3b926 view: add has_branch(name), replace some of get_branch(name) callers
get_branch(name) will be removed soon.
2023-10-13 18:12:45 +09:00
Yuya Nishihara
d840610bad view: rewrite set_branch() callers in tests, remove the method
I'm going to reorganize the underlying data structure, and set_branch() won't
be as simple as it is now.
2023-10-13 18:12:45 +09:00
Martin von Zweigbergk
0aa5f1ae10 working copy: rename working_copy_path() to just path()
It seems pretty clear from the context. Turns out we only use the
function in a test case. Maybe we don't even need it. It's easy to
provide it, though.
2023-10-12 16:10:38 -07:00
Martin von Zweigbergk
9e43207911 working copy: don't expose TreeStateError in LocalWorkingCopy API
The `TreeStateError` type is specific to the current local-disk
working-copy backend, so it should not be part of the generic
working-copy interface I'm trying to create.
2023-10-12 16:10:38 -07:00
Martin von Zweigbergk
0e09d53ce6 working copy: make some reset errors less specific
Same reasoning as the previous commits.
2023-10-12 16:10:38 -07:00
Martin von Zweigbergk
645be615b4 working copy: make some snapshot errors less specific
Same reasoning as the previous commit.
2023-10-12 16:10:38 -07:00
Martin von Zweigbergk
324c40d4c5 working copy: make some checkout errors less specific
I think some of the errors variants in `CheckoutError` are too
specific to the local-disk implementation. Let's merge them and make
them less specific, so it's easier to define a reasonable trait for
the working copy.
2023-10-12 16:10:38 -07:00
Martin von Zweigbergk
33d27ed09f working copy: start defining a working copy trait
This just extracts a trait for the trivial bits to start with.
2023-10-12 16:10:38 -07:00
Yuya Nishihara
69a30b47af refs: migrate classify_branch_push_action() to local/remote targets pair 2023-10-12 16:50:09 +09:00
Yuya Nishihara
420bf79217 view: add method to iterate local/remote pairs of certain remote
This is common operation, and we don't need a map of all remote targets
to calculate push action.
2023-10-12 16:50:09 +09:00
Yuya Nishihara
679a591a22 repo: rewrite diffing of named refs to compare each targets pair
As I'm going to split branches into per-remote map, .get_branch(name) will need
to gather remote branches by name to construct remote_targets map. Let's instead
iterate local/remote branches separately. I also migrated diffing of the other
kinds of refs to filter out unchanged entries upfront.
2023-10-11 19:24:24 +09:00
Yuya Nishihara
6bc19bfa95 git: remove workaround for "branch forget && git fetch"
As we now diff incoming git refs against our known remote branches, the problem
described in the comment no longer occurs. test_branch_forget_fetched_branch()
passes, and the inline comments in the test are still valid.
2023-10-11 06:18:36 +09:00
Yuya Nishihara
7d78ef60d1 git: rewrite diffing of exportable branches to not re-lookup targets by name
As we need to build a set of all branch names anyway, we can also put old/new
targets there. InvalidGitName is moved to caller since the diff function no
longer converts RefName to "refs/" string.
2023-10-11 06:18:36 +09:00
Yuya Nishihara
6f5cc2fd32 git: on export_refs(), copy already-exported local branches to "git" remote
This ensures that our view of the "git" remote is updated even if the last
imported/exported git_refs were out of sync because of "op restore".
2023-10-11 06:18:36 +09:00
Yuya Nishihara
aaf1bbcb4a git: use HashMap to track failed branches internally in export_refs()
This helps to filter out unexported refs in the next commit.
2023-10-11 06:18:36 +09:00
Yuya Nishihara
e160970b79 git: migrate import_refs() to diffing git_refs and known remote refs 2023-10-09 22:31:20 +09:00
Yuya Nishihara
36ee24379b git: build separate lists of git/remote refs to be imported
The idea is that the "remote" refs could have been "op restore"-d whereas
view.git_refs() will never be. The next commit will update known_remote_refs
to be constructed from the current remote branches.

Instead of building these lists in a single loop, we could load new git_refs
to the view first, and then build diffs of the remote refs. I considered that,
but I feel it would be a bit awkward to update refs before importing commits
to the view.

The "remote" refs are stored in BTreeMap since merging order should be stable.
2023-10-09 22:31:20 +09:00
Yuya Nishihara
5bd2ab76f4 git: check reserved remote name while diffing
As I'm going to add separate lists of changed git_refs/remote_refs, it'll
become a bit unclear which one we should check for reserved remotes. The
diff might also be reorganized as a list of (remote, name, kind, old_target,
new_target) where remote == "git" means the git-tracking branch. In this
data structure, the notion of reserved remote name would be lost.
2023-10-09 22:31:20 +09:00
Yuya Nishihara
f062df6da7 git: rename local variables in import_refs()
I'm going to add separate lists of changes for git_refs and remote refs, and
the current changed_git_refs will be the list for the remote refs.
2023-10-09 22:31:20 +09:00
Martin von Zweigbergk
1b9a3e27e0 merged_tree: read before/after trees concurrently
I'm going to rewrite `TreeDiffIterator` to fetch one level (depth) of
the tree at a time and concurrently. One step towards that is to
convert the iterator to a `Stream`. I'd like to do that by making the
current `Iterator` implementation call the new `Stream`
implementation. However, we can't call `futures::executor::block_on()`
on a future that itself calls `futures::executor::block_on()` (as
`Store::read_tree()` does), so the first step is to bubble up the
async-ness a bit. This patch does that by fetching both sides of the
diff concurrently. That should give close to a 2x speedup on
high-latency backends. (It doesn't help with our backend at Google,
however, because we have a daemon process that does some speculative
prefetching that usually downloads the child trees anyway.)
2023-10-08 23:36:49 -07:00
Martin von Zweigbergk
815cf9bf07 merge: implement Default and Extend on MergeBuilder
`futures::stream::Stream::collect()` requires a collection that
implements `Default` and `Extend`, and I would like to to be able to
collect a stream of trees.
2023-10-08 23:36:49 -07:00
Martin von Zweigbergk
5174489959 backend: make read functions async
The commit backend at Google is cloud-based (and so are the other
backends); it reads and writes commits from/to a server, which stores
them in a database. That makes latency much higher than for disk-based
backends. To reduce the latency, we have a local daemon process that
caches and prefetches objects. There are still many cases where
latency is high, such as when diffing two uncached commits. We can
improve that by changing some of our (jj's) algorithms to read many
objects concurrently from the backend. In the case of tree-diffing, we
can fetch one level (depth) of the tree at a time. There are several
ways of doing that:

 * Make the backend methods `async`
 * Use many threads for reading from the backend
 * Add backend methods for batch reading

I don't think we typically need CPU parallelism, so it's wasteful to
have hundreds of threads running in order to fetch hundreds of objects
in parallel (especially when using a synchronous backend like the Git
backend). Batching would work well for the tree-diffing case, but it's
not as composable as `async`. For example, if we wanted to fetch some
commits at the same time as we were doing a diff, it's hard to see how
to do that with batching. Using async seems like our best bet.

I didn't make the backend interface's write functions async because
writes are already async with the daemon we have at Google. That
daemon will hash the object and immediately return, and then send the
object to the server in the background. I think any cloud-based
solution will need a similar daemon process. However, we may need to
reconsider this if/when jj gets used on a server with a custom backend
that writes directly to a database (i.e. no async daemon in between).

I've tried to measure the performance impact. That's the largest
difference I've been able to measure was on `jj diff
--ignore-working-copy -s --from v5.0 --to v6.0` in the Linux repo,
which increases from 749 ms to 773 ms (3.3%). In most cases I've
tested, there's no measurable difference. I've tried diffing from the
root commit, as well as `jj --ignore-working-copy log --no-graph -r
'::v3.0 & author(torvalds)' -T 'commit_id ++ "\n"'` (to test a
commit-heavy load).
2023-10-08 23:36:49 -07:00
Martin von Zweigbergk
bd5eef9c5e git_backend: rename some store variables backend in tests
This is to avoid confusion with instances of the `Store` type.
2023-10-08 23:36:49 -07:00
Martin von Zweigbergk
b9a122ffe7 working_copy: inline apply_diff closure
This effectively undoes d8a313cdd4, which is no longer needed since
we just changed that error handling. It should make it easier to share
some of the current if/else blocks.
2023-10-07 14:02:31 -07:00
Martin von Zweigbergk
44eb902171 working_copy: don't crash when updating and tracked file exits on disk
Before this patch, when updating to a commit that has a file that's
currently an ignored file on disk, jj would crash. After this patch,
we instead leave the conflicting files or directories on disk. We
print a helpful message about how to inspect the differences between
the intended working copy and the actual working copy, and how to
discard the unintended changes.

Closes #976.
2023-10-07 14:02:31 -07:00
Martin von Zweigbergk
4601c87710 working_copy: move creation of parent dirs to one place
I'm about to add handling of parent dirs that are existing ignored
files, so it's better to have it in one place. The only functional
difference should be that we now create parent directories for git
submodules. I don't think that matters.
2023-10-07 14:02:31 -07:00
Martin von Zweigbergk
187ba9430a working_copy: rename to local_working_copy
It's about time we make the working copy a pluggable backend like we
have for the other storage. We will use it at Google for at least two
reasons:

 * To support our virtual file system. That will be a completely
   separate working copy backend, which will interact with the virtual
   file system to update and snapshot the working copy.

 * On local disk, we need to tell our build system where to find the
   paths that are not in the sparse patterns. We plan to do that by
   wrapping the standard local working copy backend (the one moved in
   this commit), writing a symlink that points to the mainline commit
   where the "background" files can be read from.

Let's start by renaming the exising implementation to
`local_working_copy`.
2023-10-07 08:19:03 -07:00
Yuya Nishihara
d0bc34e0f2 git: look up "git" remote branches normally 2023-10-07 19:33:35 +09:00
Yuya Nishihara
717d0d3d6d git: on deserialize/import/export, copy refs/heads/* to remote named "git"
I've added a boolean flag to the store to ensure that the migration never runs
more than once after the view gets "op restore"-d. I'll probably reorganize the
branches structure to support non-tracking branches later, but updating the
storage format in a single commit would be too involved.

If jj is downgraded, these "git" remote refs would be exported to the Git repo.
Users might have to remove them manually.
2023-10-07 19:33:35 +09:00
Yuya Nishihara
9407d4ecca view: on deserialize, remove reserved "git" remote refs stored by old jj
I'm going to migrate "refs/heads/" branches to .remote_targets["git"]. This
commit will simplify the story as we won't have to exclude "refs/remotes/git/"
refs when diffing or renaming/removing remote.
2023-10-07 19:33:35 +09:00
Yuya Nishihara
0e82e52c3a revset: remove extra step to resolve full commit id, use prefix matching
Since both has_id() and resolve_prefix() do binary search, their costs are
practically the same. I think has_id() would complete with fewer ops, but such
level of optimization wouldn't be needed here. More importantly, this ensures
that unreachable commits aren't imported by GitBackend::read_commit().
2023-10-07 02:08:36 +09:00
Yuya Nishihara
f0ad1f53ea git_backend: on read_commit(), fall back to importing extras as needed
One problematic scenario is that we have commits imported by old jj, and all
of their descendant commits are created by jj. Therefore import_head_commits()
wouldn't reach the old ancestor commits.

This change might bury a real bug, but I don't have a better alternative. Maybe
we can remove this hack after a couple of jj releases, and add a debug command
that imports all reachable Git commits from all historical heads.

Closes #2343
2023-10-07 02:08:36 +09:00
Yuya Nishihara
a302090de9 git: add hack to unset Git HEAD by using placeholder ref
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
2023-10-05 01:32:48 +09:00
Yuya Nishihara
7ccbc0424c git: extract function that resets Git HEAD
I'll add a workaround for the root parent issue #1495 there. We can pass in
the wc parent id instead of the wc_commit object, but we might want to use
wc_commit.id() to generate a unique placeholder ref name.
2023-10-04 01:43:34 +09:00
Yuya Nishihara
7c96cead34 git_backend: rename git_repo_clone() as it isn't just cloning, propagate error
Since git2::Repository::open() will access to the filesystem, it can technically
fail.
2023-10-04 00:04:24 +09:00
Yuya Nishihara
837dc4f47c git_backend: rewrite remaining git_repo() callers, make it private
While debugging git issues, I often ended up creating a deadlock by adding
debug prints. It's also not obvious that git::export_refs() works even if the
git_repo() has already been locked, whereas git::import_refs() wouldn't. Let's
consolidate lock handling to the backend implementation.
2023-10-04 00:04:24 +09:00
Yuya Nishihara
0d63223dad git_backend: proxy git2::Repository methods to manage lock scope internally
Since git_repo() acquires Mutex, it's super easy to create a deadlock.
2023-10-04 00:04:24 +09:00
Yuya Nishihara
65ecac10e9 git: exclude hidden commits from list of commits to be abandoned
This wasn't a problem before, but we wouldn't want to report previously-hidden
commits as abandoned.
2023-10-02 17:31:05 +09:00
Yuya Nishihara
16d3bcd4c5 git: make import_refs() return abandoned commits to caller
It'll be reported to user.
2023-10-02 17:31:05 +09:00
Martin von Zweigbergk
58de7c292b cli: redefine default log revset using immutable_heads()
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.
2023-10-01 11:15:30 -07:00
Yuya Nishihara
f0f1d72cf3 view: add method that iterates remote branches only
There aren't many callers, but let's add it for consistency.
2023-09-30 12:02:35 +09:00
Yuya Nishihara
c5474ff505 view: extract method that iterates local branches only
I'll probably reorganize the local/remote branches structure, so let's
minimize call sites which rely on the BranchTarget struct.
2023-09-30 12:02:35 +09:00
Yuya Nishihara
520f692a46 git: on import_refs(), don't preserve old branches referenced by remote refs
If we made @git branches real .remote_targets["git"], remotely-rewritten
commits could also be pinned by the @git branches, and therefore wouldn't be
abandoned. We could exclude the "git" remote, but I don't think local commits
should be pinned by remote refs in general. If we squashed a fetched commit,
remote ref would point to a hidden commit anyway.
2023-09-30 12:02:35 +09:00
Martin von Zweigbergk
7fda80fc22 tree: simplify conflict before resolving at hunk level
I ran into a bug the other day where `jj status` said there was a
conflict in a file but there were no conflict markers in the working
copy. The commit was created when I squashed a conflict resolution
into the commit's parent. The rebased child commit then ended up in
this state. I.e., it looked something like this before squashing:

```
C (no conflict)
|
| B conflict
|/
A conflict
```

The conflict in B was different from the conflict in A. When I
squashed in C, jj would try to resolve the conflicts by first creating
a 7-way conflict (3 from A, 3 from B, 1 from C). Because of the exact
content-level changes, the 7-way conflict couldn't be automatically
resolved by `files::merge()` (the way it currently works
anyway). However, after simplifying the conflict, it could be
resolved. Because `MergedTree::merge()` does another round of conflict
simplification of the result at the end of the function, it was the
simplifed version that actually got stored in the commit. So when
inspecting the conflict later (e.g. in the working copy, as I did), it
could be automatically resolved.

I think there are at least two ways to solve this. One is to call
`merge_trees()` again after calling `tree.simplify()` in
`MergedTree::merge()`. However, I think it would only matter in the
case of content-level conflicts. Therefore, it seems better to make
the content-level resolution solve this case to start with. I've done
that by simplifying the conflict before passing it into
`files::merge()`. We could even do the fix in `files::merge()`, but
doing it before calling it has the advantage that we can avoid reading
some unchanged content from the backend.
2023-09-27 22:14:39 -07:00
Martin von Zweigbergk
af80e4e407 files: take Merge argument to merge()
All non-test callers already have a `Merge` object, so let's pass that
instead. We thereby simplify the callers a little, and we enforce the
"adds.len() == removes.len() + 1" constraint in the type.
2023-09-27 22:14:39 -07:00
Martin von Zweigbergk
e50f6acab1 templater: fast-path empty and conflict to not read trees
When there's a single parent, we can determine if a commit is empty by
just comparing the tree ids. Also, when using tree-level conflicts, we
don't need to read the trees to determine if there's a conflict. This
patch adds both of those fast paths, speeding up `jj log -r ::main`
from 317 ms to 227 ms (-28.4%). It has much larger impact with our
cloud-based backend at Google (~5x faster).

I made the same fix in the revset engine and the Git push code (thanks
to Yuya for the suggestion).
2023-09-26 18:18:52 -07:00
Martin von Zweigbergk
a6ef3f0b6c cli: make set of immutable commits configurable
This adds a new `revset-aliases.immutable_heads()s` config for
defining the set of immutable commits. The set is defined as the
configured revset, as well as its ancestors, and the root commit
commit (even if the configured set is empty).

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

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

For now, the functionality is in the CLI library. I'm not sure if we
want to move it into the library crate. I'm leaning towards letting
library users do whatever they want without being restricted by
immutable commits. I do think we should move the functionality into a
future `ui-lib` or `ui-util` crate. That crate would have most of the
functionality in the current `cli_util` module (but in a
non-CLI-specific form).
2023-09-25 15:41:45 -07:00
Martin von Zweigbergk
551abee1d6 local_backend: don't write commits with no parents 2023-09-25 15:41:45 -07:00
Yuya Nishihara
0ca5cf48db git: make fetch() import local tags in addition to remote branches 2023-09-26 00:47:00 +09:00
Martin von Zweigbergk
380e204e73 test: use test backend in most remaining tests too
I don't think the backend should matter for any of these tests, so
let's test with only one, and let's make that the strictest one - the
new test backend.

This reduces the number of tests by 74 (from 974 to 900), but saves no
measurable run time.
2023-09-24 21:24:01 -07:00
Martin von Zweigbergk
9d8be29d94 watchman: use single-threaded async runtime
The `#[tokio::main]` annotation uses a multi-threaded runtime by
default. We don't need that for querying watchman. Switching to the
single-threaded runtime saves about 20 ms.
2023-09-24 15:46:13 -07:00
Yuya Nishihara
9697970f71 revset: simplify RevsetAliasesMap getters to not construct id
I originally attempted to embed function parameters in RevsetAliasId. That's
probably why these getters return id. Let's move id construction to callers
since the id only serves as a recursion blocker.
2023-09-24 23:21:23 +09:00
Martin von Zweigbergk
acf84f5cd8 cargo: add LICENSE file to each crate we publish
According to https://github.com/clap-rs/clap/pull/2810, the Apache
license must be distributed with sources, including with the sources
we publish to crates.io.
2023-09-22 21:48:28 -07:00
Martin von Zweigbergk
9946e52fdf tree: leverage Merge::try_map() when reading file contents to merge 2023-09-22 19:33:48 -07:00
Yuya Nishihara
4974065edb git: update comment about remote-tracking branches to be exported
Spotted while porting it to per-remote views. Undone fetch/push is tricky. If
we want to detect git/jj conflicts in that scenario, we would need to track
both "known" and "current" remote targets. It's probably okay to export jj's
remote targets as we do the reverse for import_refs(), but I need to think
that a bit more.
2023-09-23 09:56:56 +09:00
Martin von Zweigbergk
6f53d3a103 tests: test views, operations, and mutable repos only with test backend
I don't think the backend type should matter for any of these.
2023-09-20 07:47:30 -07:00
Martin von Zweigbergk
e3f82cd99a tests: leverage TestRepo::init() in test_merged_tree
I forgot to update these call sites when I introduced (the new version
of) `TestRepo::init()`.
2023-09-20 07:47:30 -07:00
Martin von Zweigbergk
f39b0d24c8 tests: use test backend in working copy tests, fix MergedTree bug
Only tests dealing with Git submodules care about the backend type.

Switching the tests to use the test backend also uncovered another bug
in `MergedTree`, so I fixed that too. The bug only happens with legacy
trees (path-level conflicts) and backends that care about the conflict
path, so it wouldn't happen with Git backends, and it wouldn't happen
at Google either (because we use tree-level conflicts).
2023-09-19 20:49:41 -07:00
Martin von Zweigbergk
0f7054e8c3 tests: wherever we test with only one backend, use the test backend
I don't think there's any reason to use the local backend in tests
instead of using the stricter test backend.

I think we should generally use the test backend in tests and only use
the local backend or git backend when there's a particular reason to
do so (such as in `test_bad_locking` where the on-disk directory
structure matters). But this patch only deals with the simpler cases
where we were only testing with the local backend.
2023-09-19 20:49:41 -07:00
Martin von Zweigbergk
d575aaeca8 backend: move constant functions first
`root_commit_id()`, `root_change_id()`, and `empty_tree_id()` were
strangely ordered between `write_symlink()` and `read_tree().
2023-09-19 05:24:51 -07:00
Martin von Zweigbergk
7ecd64fde1 merged_tree: use child path when merging child
This fixes a bug where we used the parent directory's path when trying
read trees and files for a child entry. Many tests in
`test_merged_tree` fail after switching to the test backend there
without this fix/
2023-09-18 07:53:19 -07:00
Martin von Zweigbergk
63ba2a6346 tests: add a strict backend for use in tests
We ran into a bug in `MergedTree` with our commit backend at
Google. The problem there was that `MergedTree` sometimes uses the
wrong path when reading files and trees. We didn't catch the bug in
our tests (outside of Google) because both our backends let you read
files and trees at any path.

This commit introduces a stricter backend that we can use in tests to
catch this kind of bug. For simplicity, it stores all data in
memory. Since tests are short-lived, I think that should be fine.

For now, this backend is stricter only in that it doesn't mix objects
written to different paths. We can make it strict/lossy in other ways
later (e.g. modifying written commit objects).

I think having a backend designed for tests can also be useful for
later making it possible to control the backend, e.g. to inject
errors.

We may want to replace almost all uses of the local backend in tests
with uses of this new test backend.
2023-09-18 07:53:19 -07:00
Martin von Zweigbergk
cfffbb6cd7 content_hash: make public
I'm going to use `content_hash` in a new commit backend for use in
tests.
2023-09-18 07:53:19 -07:00
Martin von Zweigbergk
9c30d7500b testutils: delete bool-typed init() in favor of enum-typed version
It makes the call sites clearer if we pass the `TestRepoBackend` enum
instead of the boolean `use_git` value. It's also more extensible (I
plan to add another backend for tests).
2023-09-18 07:15:37 -07:00
Martin von Zweigbergk
50596c499e testutils: allow passing TestRepoBackend to TestWorkspace too 2023-09-18 07:15:37 -07:00
Martin von Zweigbergk
c6cf9d54f6 testutils: add an enum for TestRepo backend
I plan to add another backend for use in tests.
2023-09-18 07:15:37 -07:00
Martin von Zweigbergk
79527d707c testutils: use .jj-internal git repos in most tests
I don't think there's much reason to run most tests with a `.git`
directory outside of `.jj`. I think it's just that way for historical
reasons. It's been that way since I added support for `.jj`-internal
repos in a8a9f7dedd.

The reason I want to switch is to make it a little easier to create
test repos for different backends. The problem with `.jj`-external git
repos is that they depend on an additional path.

I had to update `test_bad_locking.rs` to make the code merging
directories able handle missing directories on some side, because
git's loose objects result in directories getting created on one or
both sides.
2023-09-18 07:15:37 -07:00
Martin von Zweigbergk
2bc641c57c test_bad_locking: make merge_directories() expect non-existent target
I ran into some issues here when switching our tests to use
`.jj`-internal git repos. For example, the `std::fs::copy()` calls
started failing, which may be related to #2103. I think one problem is
that we could end up calling `merge_directories()` twice for the same
directory. This patch fixes that by deduping the paths we call with,
and makes the function assume that the output directory doesn't exist.
2023-09-18 06:59:28 -07:00
Yuya Nishihara
fc86d91b15 tests: fix test_merge_views_branches() to set up local "feature" branches
Git refs aren't involved in this test.
2023-09-11 01:07:29 +09:00
Yuya Nishihara
b80d04319d tests: rewrite "@"/"root" revset resolution tests without using bad git refs 2023-09-11 01:07:29 +09:00
Martin von Zweigbergk
86206e83a4 repo_path: avoid repeated copying of PathBuf in to_fs_path()
I've noticed `WorkspaceCommandHelper::format_file_path()` appear in
profiles a few times. A big part of that is spent in
`RepoPath::to_fs_path()`. I think I had been thinking that
`PathBuf::join()` takes `self` by value and mutates it, but it turns
out it creates a new instance. So our `result = result.join(...)` in a
loop was copying the `PathBuf` over and over. This fixes that and also
reserves the expected size. That speeds up `jj files
--ignore-working-copy -r v6.0` in the Linux repo from 546.0 s to 509.3
s (6.7%).
2023-09-09 08:43:29 -07:00
Yuya Nishihara
d05aaf30b4 git: promote imported commits to tree-conflict format if configured 2023-09-08 21:54:43 +09:00
Yuya Nishihara
0c4e667e48 git: import new head commits all at once 2023-09-08 21:54:43 +09:00
Yuya Nishihara
f744e5920b git: create no-gc ref by GitBackend
Since we now have an explicit method to import heads, it makes more sense to
manage no-gc refs by the backend.
2023-09-08 21:54:43 +09:00
Yuya Nishihara
17f502c83a git: do not import unknown commits by read_commit(), use explicit method call
The main goal of this change is to enable tree-level conflict format, but it
also allows us to bulk-import commits on clone/init. I think a separate method
will help if we want to provide progress information, enable check for
.jjconflict entries under certain condition, etc.

Since git::import_refs() now depends on GitBackend type, it might be better to
remove git_repo from the function arguments.
2023-09-08 21:54:43 +09:00
Yuya Nishihara
dde1c1ec9c revset: use ancestors(x, 0) in tree substitution test 2023-09-08 09:28:14 +09:00
Yuya Nishihara
8b825796ff cli: rewrite "x | x-" in default log revset as "ancestors(x, 2)" 2023-09-08 09:28:14 +09:00
James Sully
0946934ca6 revset: Add optional argument n to ancestors() in revset language 2023-09-08 02:50:58 +10:00
James Sully
6ea10906a8 revset: add ancestors_range() method 2023-09-08 02:50:58 +10:00
Yuya Nishihara
c4769e0b7c revset: translate symbol rules in error message
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.
2023-09-07 15:29:39 +09:00
Yuya Nishihara
a868b2d9a5 revset: do not lookup unimported tags or remote branches by unqualified name
Since e7e49527ef "git: ensure that remote branches never diverge", the last
known "refs/remotes" ref should be synced with the corresponding remote branch.
So we can always trust the branch@remote expression. We don't need "refs/tags"
lookup either since tags should have been imported by git::import_refs().

FWIW, I'm thinking of reorganizing view.git_refs() map as per-remote views.
It would be nice if we can get rid of revsets and template keywords exposing
low-level Git ref primitives.
2023-09-06 13:42:22 +09:00
Yuya Nishihara
4e6323c0a4 revset: add basic test for tag name resolution 2023-09-06 13:42:22 +09:00
Yuya Nishihara
8f4512b109 git: rename local variables in diff_refs_to_export()
old/new_branch sounds like a branch name, but it's a RefTarget. And
jj_known_refs_passing_filter may contain "new" ref names.
2023-09-06 08:17:49 +09:00
Yuya Nishihara
119cc88832 git: extract calculation step from export_some_refs()
export_some_refs() is big, let's split it to functions of reasonable size.
2023-09-06 08:17:49 +09:00
Yuya Nishihara
89f1d83a22 git: do not export new ref if racy process created one with the same name
Since we've checked the ref doesn't exist in this code path, I think it's
better to not overwrite the existing ref.
2023-09-06 08:17:49 +09:00
Yuya Nishihara
a4dd598e3e git: extract helper that exports single updated ref 2023-09-06 08:17:49 +09:00
Yuya Nishihara
d1a08782c9 git: extract helper that exports single deleted ref 2023-09-06 08:17:49 +09:00
Yuya Nishihara
7282b517e8 git: remove stale TODO comment about FailedRefExportReason 2023-09-06 08:17:49 +09:00
Philip Metzger
8dda7e21e0 revsets: Add descendants_at and ancestors_at
They are needed for `next` and `prev`.
They behave symmetrically for parents and children.
2023-09-05 23:13:39 +02:00
Martin von Zweigbergk
f198a1e5f9 git: skip branches on root commit on export
Git doesn't have a root commit, so we should skip branches pointing to
it on export, just like we do with conflicted branches (which Git also
doesn't support).
2023-09-04 20:08:11 -07:00
Martin von Zweigbergk
ef550a9d6d git: include reason for each failed ref export 2023-09-04 20:08:11 -07:00
Martin von Zweigbergk
f6c24fbcef git: return refs that failed to export in sorted order
Before this patch, the order would depend on the reason we failed to
export a ref, because we would add to the `failed_branches` list in
several different places. What's worse, when the export failed because
the branch was conflicted or had an invalid name (from Git's
perspective), it was non-deterministic because we iterated over a
HashSet. This patch fixes that by sorting at the end.

Note that we still want the `branches_to_update` map to be a
`BTreeMap` so we update branches in deterministic order. Otherwise the
error when trying to export both branches `main` and `main/sub` will
become non-deterministic.
2023-09-04 20:08:11 -07:00
Yuya Nishihara
b0c8e9ef62 revset: add 0-ary "::" and ".." operators as short for "all()" and "~root()"
Suppose "x::y" is the operator that defaults to "root()::visible_heads()"
respectively, "::" is identical to "all()". Since we've just changed the
behavior of "..y", ".." is now "root()..visible_heads()" meaning "~root()".
2023-09-05 10:40:04 +09:00
Yuya Nishihara
6b2ad23f8f revset: evaluate "..y" expression to "root()..y"
This seems useful since the root commit is often uninteresting. It's also
consistent with "x::y" in a way that the left operand defaults to "root()".
2023-09-05 10:40:04 +09:00
Yuya Nishihara
f2c1697362 revset: fix comment about "@" expression 2023-09-05 10:40:04 +09:00
Yuya Nishihara
e3c85d6ecc revset: convert root symbol to function
The idea is that we can fully eliminate special symbols that would otherwise
shadow user branches, tags, or change ID prefixes.

Closes #2095
2023-09-04 10:36:30 +09:00
Martin von Zweigbergk
e3825495e3 store: don't include cached objects in Debug format
I was looking into some overly verbose logs and happened to notice
that `Store` uses the default derived, which presumably means it's
going to include all objects in its cached. Just including the
backend's debug string seems enough.
2023-09-01 16:49:23 -07:00
Martin von Zweigbergk
16c897d8b4 conflicts: leverage Merge::map() in materialize_merge_result() 2023-09-01 16:23:39 -07:00
Martin von Zweigbergk
b8f71a4b30 working_copy: in LockedWorkingCopy::drop(), discard unsaved changes
In `LockedWorkingCopy::drop()`, we panic if the caller had not called
`finish()`. IIRC, the idea was both to find bugs where we forgot to
call `finish()` and to prevent continuing with a modified
`WorkingCopy` instance. I don't think the former has been a problem in
practice. It has been a problem in practice to call `discard()` to
avoid the panic, though. To address that, we can make the `Drop`
implementation discard the changes (forcing a reload of the state if
the working copy is accessed again).
2023-09-01 12:25:47 -07:00
Martin von Zweigbergk
5ef0be73c1 merged_tree: allow building trees with variable-arity overrides
When restoring (`jj restore`) a 3-sided conflict from one tree into a
2-sided tree (or a resolved tree), we'll need to extend the size arity
of the target tree to that of the source tree. I had not considered
this case before. This patch relaxes the constraint in
`MergedTreeBuilder` to allow such cases. The additional trees are
based on empty trees with only the larger overrides in them.
2023-09-01 06:09:37 -07:00
Martin von Zweigbergk
d29c831ef9 merge: add a function for padding a Merge to a given size
This will be useful for padding merge objects in `MergedTreeBuilder`
to the size of the widest override.
2023-09-01 06:09:37 -07:00
Martin von Zweigbergk
be08707a3a op_heads_store: propagate errors from OpStore 2023-08-31 12:36:47 -07:00
Martin von Zweigbergk
9d9b2cd057 tests: leverage create_tree() in a few more tests 2023-08-30 19:58:42 -07:00
Martin von Zweigbergk
26581750fe store: add a empty_merged_tree_id() helper
Many (most?) callers of `Store::empty_tree_id()` really want a
`MergedTreeId`, so let's create a helper for that. It returns the
`Legacy` variant, which is what all current callers used. That should
be all we need since the two variants compare equal these days, and
since trees built based on the legacy variant can get promoted to the
new variant on write if the config is enabled.
2023-08-30 19:58:42 -07:00
Martin von Zweigbergk
61501db8ec merged_trees: consider conflict-format-change-only commits empty
When we start writing tree-level conflicts in an existing repo, we
don't want commits that change the format to be non-empty if they
don't change any content. This patch updates `MergeTreeId::eq()` to
consider two resolved trees equal even if only their `MergedTreeId`
variant is different (one is path-level and one is tree-level).

I think I've gone through all places we compare tree ids and checked
that it's safe to compare them this way. One consequence is that
rebasing a commit without changing the parents (typically
auto-rebasing after `jj describe`) will not lead to the tree id
getting upgraded, due to an optimization we have for that case. I
don't think that's serious enough to handle specially; we'll have to
support the old format for existing repos for a while regardless of a
few commits not getting upgraded right away.

The number of failing tests with the config option enabled drop from
108 to 11 with this patch.
2023-08-30 06:17:21 -07:00
Martin von Zweigbergk
8e47d2d66f merged_tree: add config option to write trees using new format
We're finally ready to start writing trees using the new format where
we represent conflicts by having multiple trees in the commit instead
of having a single tree with multiple entries at a path. This patch
adds a config option for that. It's not ready to be used yet, so I
haven't updated the release notes or other documentation.

I added only a simple CLI test for testing what happens when the
config is enabled in an existing repo. 108 tests currently fail if we
flip the default.
2023-08-30 06:17:21 -07:00
Martin von Zweigbergk
bb755ae754 working_copy: use MergedTreeBuilder in test 2023-08-30 06:17:21 -07:00
Martin von Zweigbergk
6b3fa3ee79 working_copy: avoid a nested TreeBuilder in a test
It's just slightly simpler to not create a `TreeBuilder` to create a
`TreeValue`; we can instead write to the outer builder.
2023-08-30 06:17:21 -07:00
Martin von Zweigbergk
7e6930b56f backend: remove last few instances of MergedTreeId::as_legacy_tree_id() 2023-08-30 06:17:21 -07:00
Martin von Zweigbergk
962da1947e tests: make dump_tree() work with merged trees
My goal is to minimize impact on tests when we start using the new
format.
2023-08-30 06:17:21 -07:00
Waleed Khan
56c61fd047 merge_tools: create builtin diff editor 2023-08-30 05:38:10 -04:00
Martin von Zweigbergk
145b0b24d8 commit: drop merged_ prefix from tree() and tree_id()
The old `tree()` and `tree_id()` functions are now gone, so we can use
those names for the new functions.
2023-08-29 08:32:04 -07:00
Martin von Zweigbergk
f54b456e64 working_copy: leverage Store::get_root_tree() in current_tree()
I guess I forgot this when I added `Store::get_root_tree()`.
2023-08-29 08:32:04 -07:00
Martin von Zweigbergk
2d50d8a077 merged_tree: propagate errors from from_legacy_tree() 2023-08-29 08:32:04 -07:00
Martin von Zweigbergk
67832a3940 merged_tree: take store argument to write_tree() instead of new()
The store isn't needed until we write the trees, so I think it makes
more sense to pass it there.
2023-08-29 08:32:04 -07:00
Martin von Zweigbergk
64b47bae56 tree: inline legacy_id() into its sole caller 2023-08-29 07:01:52 -07:00
Martin von Zweigbergk
d9ce70c176 tests: make create_tree() return MergedTree
I think most tests want a `MergedTree`, so this makes `create_tree()`
return that. I kept the old function as `create_single_tree()`. That's
now only used in `test_merge_trees` and `test_merged_tree`.

I also consistently imported the functions now, something I've
considered doing for a long time.
2023-08-29 07:01:52 -07:00
Martin von Zweigbergk
e4c6595620 tests: make create_random_tree() return a MergedTreeId 2023-08-29 07:01:52 -07:00
Martin von Zweigbergk
9c77e6aa8c commit_builder: remove last traces of pre-MergedTree API 2023-08-29 07:01:52 -07:00
Martin von Zweigbergk
6e3590f5cd tests: remove last uses of Commit:tree() and delete it 2023-08-29 07:01:52 -07:00
Yuya Nishihara
55c6e90555 git: remove handling of real remote named "git", always override
#1690
2023-08-29 22:50:46 +09:00
Yuya Nishihara
ce3d28e234 git: do not import refs from remote named "git"
I made it simply fail on explicit fetch/import, and ignored on implicit import.
Since the error mode is predictable and less likely to occur. I don't think it
makes sense to implement warning propagation just for this.

Closes #1690.
2023-08-29 22:50:46 +09:00
Yuya Nishihara
04e2f5ed20 git: fix typo in GitFetchError message 2023-08-29 22:50:46 +09:00
Yuya Nishihara
35a596ff66 git: prohibit creation of remote named "git"
#1690
2023-08-29 22:50:46 +09:00
Yuya Nishihara
0e26ef7733 git: add constant for pseudo remote name "git" 2023-08-29 22:50:46 +09:00
Martin von Zweigbergk
f47da04a43 tree: delete recursive diff iterator, which is no longer used 2023-08-28 16:21:44 -07:00
Martin von Zweigbergk
1b24b522f6 tree: move diff_summary() to MergedTree 2023-08-28 16:21:44 -07:00
Martin von Zweigbergk
a7a2150328 tree: delete unused DiffSummary::is_empty() 2023-08-28 16:21:44 -07:00
Martin von Zweigbergk
dc06bbc7d1 commit: migrate remaining uses of Commit::tree_id() and delete it 2023-08-28 15:58:34 -07:00
Martin von Zweigbergk
7bfd439bd1 rewrite: return MergedTree from merge_commit_trees_without_repo() 2023-08-28 15:58:34 -07:00
Martin von Zweigbergk
90e78a1424 rewrite: return MergedTree from merge_commit_trees() 2023-08-28 15:58:34 -07:00
Martin von Zweigbergk
bd6098e09e cli: merge trees via MergedTree in jj move 2023-08-28 15:58:34 -07:00
Martin von Zweigbergk
873a6f0674 merged_tree: add a function for merging 3 MergedTrees
With the already existing `MergedTree::resolve()` and all the recent
refactorings into `Merge<T>`, it's now very easy to add support for
3-way merging of `MergedTree` instances.
2023-08-28 15:58:34 -07:00
Martin von Zweigbergk
1674a421ec commit_builder: take MergedTreeId for root id argument 2023-08-28 15:58:34 -07:00
Martin von Zweigbergk
e5ec99d159 backout: propagate error from merging of trees 2023-08-28 15:58:34 -07:00
Martin von Zweigbergk
a7e5ea06c0 tests: make test helper for snapshotting working copy return MergedTree 2023-08-27 06:49:45 -07:00
Martin von Zweigbergk
1895a55157 working_copy: make old_checkout argument be MergedTreeId
I think this was the last piece for making the working copy handle
tree-level conflicts.
2023-08-27 06:49:45 -07:00
Martin von Zweigbergk
abf3853717 working_copy: return MergedTreeId on snapshot 2023-08-27 06:49:45 -07:00
Martin von Zweigbergk
88e9933462 working_copy: enable storing multiple tree ids in state file 2023-08-27 06:49:45 -07:00
Martin von Zweigbergk
49e32aa532 merged_tree: teach tree builder to build multiple trees 2023-08-27 06:49:45 -07:00
Martin von Zweigbergk
1577b408a6 store: add function to look up MergedTree by MergedTreeId
We'll start seeing `MergedTreeId` in more places and we'll want it to
be easy to look up the tree.
2023-08-27 06:49:45 -07:00