Commit graph

712 commits

Author SHA1 Message Date
Martin von Zweigbergk
a1ef9dc845 merged_tree: propagate backend errors in diff iterator
I want to fix error propagation before I start using async in this
code. This makes the diff iterator propagate errors from reading tree
objects.

Errors include the path and don't stop the iteration. The idea is that
we should be able to show the user an error inline in diff output if
we failed to read a tree. That's going to be especially useful for
backends that can return `BackendError::AccessDenied`. That error
variant doesn't yet exist, but I plan to add it, and use it in
Google's internal backend.
2023-10-26 06:20:56 -07:00
Martin von Zweigbergk
6ad71e658d merged_tree: rename MergedTreeValue to MergedTreeVal
I'm going to add `MergedTreeValue` as an alias for
`Merge<Option<TreeValue>>`, but we already have a type by that name in
`merged_tree`. This patch renames it away, to make room for the new
alias. I used `MergedTreeVal` for this borrowing version to be a bit
like how `str` is a borrowed version of `String`.
2023-10-26 06:20:56 -07:00
Yuya Nishihara
a6ac9b46e7 git: simply call fetch() with one or more branch name filters 2023-10-25 03:58:48 +09:00
Martin von Zweigbergk
21b11df8a9 merge: make non-conflicted debug string for Merge shorter
Resolves states are most common and the current format is pretty
verbose. Let's print it as if `Merge` were an enum with `Resolved` and
`Conflicted` variants instead.
2023-10-24 06:45:45 -07:00
Yuya Nishihara
a756333216 git: move RefName enum from view module
It's no longer used by View API.
2023-10-24 09:45:01 +09:00
Yuya Nishihara
5543f7a11c refs: merge tracking state of remote branches
Otherwise "jj op undo" can't roll back tracking states (whereas "op restore"
can.)
2023-10-24 07:13:58 +09:00
Yuya Nishihara
cfcc76571c revset: add support for glob:pattern 2023-10-21 09:55:01 +09:00
Martin von Zweigbergk
8764ad9826 conflicts: make materialization async
We need to let async-ness propagate up from the backend because
`block_on()` doesn't like to be called recursively. The conflict
materialization code is a good place to make async because it doesn't
depends on anything that isn't already async-ready.
2023-10-20 07:38:34 -07:00
Martin von Zweigbergk
e900c97618 conflicts: reduce some duplication in tests by extracting a closure 2023-10-20 07:38:34 -07:00
Yuya Nishihara
390b3208da revset: always include non-tracking remote branches in suggestion
For the same reason as the previous commit. Non-tracking remote branch
shouldn't be shadowed by a local branch of the same name.
2023-10-17 16:42:36 +09:00
Yuya Nishihara
e0965c4533 git: on push, update jj's view of remote branches without using import_refs()
This means that the commits previously pinned by remote branches are no longer
abandoned. I think that's more correct since "push" is the operation to
propagate local view to remote, and uninteresting commits should have been
locally abandoned.
2023-10-17 15:06:03 +09:00
Yuya Nishihara
58897d79c7 git: extract push function that processes branches instead of git refs
Since pushed remote branches will share the common base targets with locals,
these branches should be marked as tracking. git::push_branches() will handle
that. It looks ugly that the public GitBranchPushTargets type keeps "force"-d
branches as a separate set, but we'll need to rework that anyway when we
implement --force-with-lease behavior. So let's leave it for now.

Some of the git::push_updates() tests have been migrated to the new function.
I left a couple of basic tests for git::push_updates() because push_updates()
will be used to implement a low-level "jj git push-refs" command.
2023-10-17 15:06:03 +09:00
Yuya Nishihara
57167cefda git: on import_refs(), don't abandon ancestors of newly fetched refs
I made import_refs() not preserve commits referenced by remote branches at
520f692a46 "git: on import_refs(), don't preserve old branches referenced by
remote refs." The idea is that remote branches are weak, and commits referenced
by these refs can be freely rewritten by future local changes without moving
the refs. I don't think that's wrong, but 520f692a46 also made "new" remote
changes be abandoned by old remote refs. This problem occurs only when
git.auto-local-branch is off.

I think there are two ways to fix the problem:
 a. pin non-tracking remote branches just like local refs
 b. pin newly fetched refs in addition to local refs
This patch implements (b) because it's simpler and more obvious that the
fetched commits would never be abandoned immediately.
2023-10-17 14:49:49 +09:00
Martin von Zweigbergk
c3b45b6fd1 workspace: make working-copy type customizable
This add support for custom `jj` binaries to use custom working-copy
backends. It works in the same way as with the other backends, i.e. we
write a `.jj/working_copy/type` file when the working copy is
initialized, and then we let that file control which implementation to
use (see previous commit).

I included an example of a (useless) working-copy implementation. I
hope we can figure out a way to test the examples some day.
2023-10-16 22:33:44 -07:00
Martin von Zweigbergk
6bfd618275 workspace: load working copy implementation dynamically
This makes `Workspace::load()` look a new `.jj/working_copy/type` file
in order to load the right working copy implementation, just like
`Repo::load()` picks the right backends based on `.jj/store/type`,
`.jj/op_store/type`, etc. We don't write the file yet, and we don't
have a way of adding alternative working copy implementations, so it
will always be `LocalWorkingCopy` for now.
2023-10-16 22:33:44 -07:00
Martin von Zweigbergk
e1f00d9426 working copy: pass commit instead of tree into check_out()
Our internal working copy implementations at Google will need the
commit so they can walk history backwards until they get to a "public"
commit. They'll then use that to tell build tools and virtual file
systems to present that as a base.

I'm not sure if we'll need to update `reset()` too. It's currently
only used by `jj untrack`, which doesn't change the commit's parent,
so it wouldn't affect any history walks.
2023-10-16 22:33:44 -07:00
Martin von Zweigbergk
7c8a0a18f9 repo: define types for backend initializer functions
`ReadonlyRepo::init()` takes callbacks for initializing each kind of
backend. We called these things like `op_store_initializer`. I found
that confusing because it is not a `OpStoreFactory` (which is for
loading an existing backend). This patch tries to clarify that by
renaming the arguments and adding types for each kind of callback
function.
2023-10-16 22:33:44 -07:00
Yuya Nishihara
4cd2518be0 git: on import_refs(), respect tracking state of existing remote refs
In this commit, new behavior is tested by using in-memory view data. Data
persistence and track/untrack commands will be implemented soon.
2023-10-16 23:21:05 +09:00
Yuya Nishihara
a697175674 view: add tracking state to RemoteRef
The state field isn't saved yet. git import/export code paths are migrated,
but new tracking state is always calculated based on git.auto-local-branch
setting. So the tracking state is effectively a global flag.

As we don't know whether the existing remote branches have been merged in to
local branches, we assume that remote branches are "tracking" so long as the
local counterparts exist. This means existing locally-deleted branch won't
be pushed without re-tracking it. I think it's rare to leave locally-deleted
branches for long. For "git.auto-local-branch = false" setup, users might have
to untrack branches if they've manually "merged" remote branches and want to
continue that workflow. I considered using git.auto-local-branch setting in the
migration path, but I don't think that would give a better result. The setting
may be toggled after the branches got merged, and I'm planning to change it
default off for better Git interop.

Implementation-wise, the state enum can be a simple bool. It's enum just
because I originally considered to pack "forgotten" concept into it. I have
no idea which will be better for future extension.
2023-10-16 23:21:05 +09:00
Martin von Zweigbergk
0582893144 working copy: return Box<dyn LockedWorkingCopy> from start_mutation() 2023-10-15 16:13:19 -07:00
Martin von Zweigbergk
580586d008 working copy: return Box<dyn WorkingCopy> from finish() 2023-10-15 16:13:19 -07:00
Martin von Zweigbergk
6a13fa8264 working copy: add tree_id() to backend trait
Looks like I missed this earlier. I think it makes sense to have on
all working copy implementations.
2023-10-15 16:13:19 -07:00
Martin von Zweigbergk
63654d064b working copy: add sparse pattern functions to backend trait 2023-10-15 15:59:49 -07:00
Martin von Zweigbergk
0d2247b0df working copy: add check_out() function to the backend trait
This includes documenting the new function and the other types moved
to the `working_copy` module.
2023-10-15 15:59:49 -07:00
Martin von Zweigbergk
781859cb51 working copy: add snapshot() function to the backend trait
This includes documenting the new function and the other types moved
to the `working_copy` module.
2023-10-15 15:59:49 -07:00
Martin von Zweigbergk
8826639e4e working copy: remove reference from locked instance to base instance
It's going to be easier to define a `LockedWorkingCopy` trait if it
doesn't need to borrow from `WorkingCopy`, so let's remove the
reference we currently have and have
`LockedLocalWorkingCopy::finish()` return the new `LocalWorkingCopy`
instead.

I think the main disadvantage is that we now have to remember to
replace the old `LocalWorkingCopy` instance by the new one, whereas
the compiler would remind us before this commit. We could make
`start_modification()` take an owned `self`, but that would be a bit
annoying to work with when we have the instance stored in a field.
2023-10-15 15:59:49 -07:00
Martin von Zweigbergk
c8184845d7 workspace: replace working_copy_mut() by wrapper type
I'm about to make `LockedLocalWorkingCopy` not borrow from
`LocalWorkingCopy`. That will make it easier to forget to update any
`LocalWorkingCopy` variables when the modifications have been
committed. This patch introduces a wrapper around
`LockedLocalWorkingCopy` to help prevent that.

Thanks to Yuya for the suggestion.
2023-10-15 15:59:49 -07:00
Martin von Zweigbergk
35f9c12cb5 working copy: move LocalWorkingCopy::check_out() to Workspace
`LocalWorkingCopy::check_out()` can be expressed using the planned
`WorkingCopy` trait, so it doesn't need to be in the trait itself
`WorkingCopy`. I wasn't sure if I should make it a free function in
`working_copy`, but I ended up moving it onto `Workspace`.
2023-10-15 15:59:49 -07:00
Martin von Zweigbergk
9fb5a4bcef tests: rename a repo1 variable to repo since there's only one
I think the `repo1` name was a leftover from when `ReadonlyRepo` had a
`WorkingCopy`.
2023-10-15 15:59:49 -07:00
Yuya Nishihara
8b46712bcb view: add stub method to set remote branch target and state, migrate callers
Since set_remote_branch_target() is called while merging refs, its tracking
state shouldn't be reinitialized. The other callers are migrated to new setter
to keep the story simple.
2023-10-16 05:12:19 +09:00
Yuya Nishihara
35ea607abd view: make get_remote_branch() return RemoteRef instead of RefTarget
I'm going to add tracking state to RemoteRef, and we should compare both
target and state in the tests.
2023-10-16 05:12:19 +09:00
Yuya Nishihara
ab1a6e2f71 view: make remote branches iterator yield RemoteRef instead of RefTarget
git::import_refs() will need to read RemoteRef's tracking state.
2023-10-16 05:12:19 +09:00
Yuya Nishihara
e7d93e5bf1 view: turn BranchTarget into borrowed type
This isn't important, but I'm going to change remote_targets to store RemoteRef
instead of RefTarget, so I went ahead and change the other field types as well.
2023-10-16 05:12:19 +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
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
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
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
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
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
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
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
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
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
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