252 Commits

Author SHA1 Message Date
Martin von Zweigbergk
ecb86118e5 working_copy: get file metadata using open file descriptor
When we have just written a file or conflict, we can get metadata for
it via the open file descriptor instead of using the path. That
removes the risk of a race where the file got removed or replaced by
another file type (at least on Unix).
2022-05-25 11:51:18 -07:00
Martin von Zweigbergk
e7aaf2f95f working_copy: pass Metadata into file_state()
This is a refactoring to prepare for getting the metadata from an
already open file when possible.
2022-05-25 11:51:18 -07:00
Martin von Zweigbergk
d451242746 working_copy: propagate backend errors on checkout 2022-05-25 11:51:18 -07:00
Martin von Zweigbergk
899325e94e working_copy: use #[from] to slightly simplify error handling 2022-05-25 11:51:18 -07:00
Martin von Zweigbergk
45c9d297c2 drop: downgrade two assertions to error messages
These assertions were there to catch bugs, but when the bugs happen,
the assertions can obsure the underlying error (as @tp-woven found out
on #258). Let's just print errors instead.
2022-05-23 15:36:18 -07:00
Martin von Zweigbergk
ffc57310f6 cargo: upgrade protobuf crates to 3.0.1
The biggest difference in the API is that fields are now public. The
exception from that is `oneof` fields, which still require setters and
getters.

I couldn't measure any difference in performance. I didn't expect any
difference either, but it's good that it didn't seem to regress. I
timed `jj debug operation <some hash prefix>`, which will read the
whole operation log (to check that the prefix is unambiguous).
2022-05-04 17:02:11 -07:00
Martin von Zweigbergk
7268e5608e working_copy: propagate errors when snapshotting
Closes #258
2022-05-02 11:23:38 -07:00
Martin von Zweigbergk
96b3e05bc5 working_copy: rename write_tree() to snapshot()
I think I copied the name `write_tree()` from Git, but I find it quite
confusing, since it's not clear if it write a tree to the working copy
or reads the working copy and writes a tree to the store (it's the
former).
2022-05-02 08:00:15 -07:00
Martin von Zweigbergk
5893b52fd1 cleanup: use while let Some(...) instead of checking before popping 2022-05-01 13:45:00 -07:00
Martin von Zweigbergk
78da5596b7 working_copy: switch an if let Some() { } else { } to a match 2022-04-26 21:19:09 -07:00
Martin von Zweigbergk
0fbb1d3971 working_copy: don't visit whole ignored tree even if last in order
When committing the working copy, we try to not visit ignored
directories (as e.g. `target/` often is), but we need to visit it if
there are already tracked files in it. I initially missed that in
c1060610bda2 and then fixed it in a028f33e3b21. The fix works by
checking if the next path after the ignored path is inside the ignore
path (viewed as a directory). However, I forgot to handle the case
where there are no paths at all after the ignored path. So, for
example, if the `target/` directory should be ignored and it there
were no tracked paths after `target/` in alphabetical order, we would
still visit the directory. That's why the bug reproduced in the
`git-branchless` repo but not in the `jj` repo (because there are
files under `testing/` and `tests/` here).

Closes #247.
2022-04-26 20:53:49 -07:00
Martin von Zweigbergk
18a64c365a working_copy: respect sparse patterns when writing tree (#52) 2022-04-26 14:52:17 -07:00
Martin von Zweigbergk
0d881de56c working_copy: allow updating sparse patterns (#52)
With this patch, we add support for setting the sparse patterns, and
we respect it when updating the working copy.
2022-04-26 14:52:17 -07:00
Martin von Zweigbergk
ceb6c152a1 working_copy: record sparse patterns in the tree state (#52)
This patch makes room for sparse patterns in the `TreeState` proto
message. We also start setting that value to a list of just the
pattern `.` when we create new working copies. Old working copies
without the sparse patterns are also interpreted as having that single
pattern. Note that this absence of sparse patterns is different from a
present list of no patterns. The latter is a valid state and means
that no paths are included in the sparse checkout.
2022-04-26 14:52:17 -07:00
Martin von Zweigbergk
ed2d2f8a4f working_copy: extract function for updating the working copy files (#52)
Updating the working copy with new sparse patterns is very similar to
updating it with a new tree. We're going to reuse this extracted
function soon.
2022-04-26 14:52:17 -07:00
Martin von Zweigbergk
53911b076b working_copy: fix crash when updating and only executable bit changed 2022-04-14 23:46:28 -07:00
Martin von Zweigbergk
f16d2a237b backend: pass in path when reading/writing conflicts as well
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.
2022-03-31 10:23:33 -07:00
Martin von Zweigbergk
d56ae79d3f working_copy: let caller pass in base Git ignores (#65, #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.
2022-03-12 10:48:06 -08:00
Martin von Zweigbergk
51da2a2dc1 gitignore: move function for chaining .gitignore to central place (#65, #87)
I want to be able to reuse the code for chaining two `.gitignore`
files outside of `working_copy.rs`.
2022-03-12 10:48:06 -08:00
Martin von Zweigbergk
e7a7cb8ea5 gitignores: remove error type that's never instantiated 2022-03-12 10:48:06 -08:00
Martin von Zweigbergk
03e6b8c0e6 working_copy: take Tree, not CommitId, as argument to check_out()
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.
2022-02-13 12:14:34 -08:00
Martin von Zweigbergk
315e5e87a3 working_copy: take a tree object instead of ID in TreeState::check_out()
The callers mostly have the tree object available anyway.
2022-02-13 12:12:08 -08:00
Martin von Zweigbergk
00c9a1ae11 working_copy: stop taking commit ID in LockedWorkingCopy::finish()
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.
2022-02-12 23:48:06 -08:00
Martin von Zweigbergk
cde3609163 working_copy: stop taking initial checkout in constructor
We don't use the value anymore.
2022-02-12 23:45:05 -08:00
Martin von Zweigbergk
993de96fc7 working_copy: stop keeping track of commit ID 2022-02-12 17:22:37 -08:00
Martin von Zweigbergk
e098c01935 working_copy: replace commit ID by tree ID for checking for changes
What matters for the working copy is the tree ID. We should be able to
remove the commit ID. This patch gets us close.
2022-02-12 17:16:19 -08:00
Martin von Zweigbergk
537b1de7d9 working_copy: move check of old commit ID on checkout to higher level
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.
2022-02-12 14:27:40 -08:00
Martin von Zweigbergk
b74851e005 working_copy: make sure discarded update is not visible
`LockedWorkingCopy::discard()` shouldn't result in changes to the
on-disk state, but `LockedWorkingCopy::check_out()` may have already
written a state file, which is surprising. The changes also remain in
memory, which is also surprising. Let's fix both of those issues.
2022-02-09 10:40:51 -08:00
Martin von Zweigbergk
c09a4e15c5 workspace: add a function for initializing additional workspace (#13) 2022-02-02 17:00:03 -08:00
Martin von Zweigbergk
fb8fbdc4b3 working_copy: keep track of workspace ID (#13)
This patch makes it so the workspace ID can be stored in
`.jj/working_copy/checkout`. The workspace ID is still always
"default".
2022-02-02 08:15:22 -08:00
Martin von Zweigbergk
38180555de working_copy: keep track of operation ID (#13)
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).
2022-01-19 19:15:29 -08:00
Martin von Zweigbergk
419efb88f9 working_copy: update stale comment on LockedWorkingCopy
The recent refactoring introducing `WorkgingCopy::start_mutation()`
(25d19e8a65b8) made this comment incorrect.
2022-01-19 15:12:45 -08:00
Martin von Zweigbergk
45a00e819d working_copy: pass only a TreeId to LockedWorkingCopy::check_out()
It only needs a `TreeId`.
2022-01-19 09:04:01 -08:00
Martin von Zweigbergk
fabaf8b608 working_copy: move logic of check_out() onto LockedWorkingCopy
Having the checkout functionality in `LockedWorkingCopy` makes it a
little more flexible (one could imagine using it for udating working
copy files and then discarding the state changes, for example). It
also lets us reuse a few lines of code for locking. I left
`WorkingCopy::check_out()` for convenience because that's what all
current users want.
2022-01-19 09:04:01 -08:00
Martin von Zweigbergk
9e1869dcef working_copy: pass in old commit ID to check_out()
`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.
2022-01-19 09:04:01 -08:00
Martin von Zweigbergk
8c97fdf5d6 working_copy: remove untrack() now that we have more flexible reset() 2022-01-19 09:04:01 -08:00
Martin von Zweigbergk
cd4fbd3565 working_copy: add a reset() function for Git-like reset
We already have two usecases that can be modeled as updating the
`TreeState` without touching the working copy:

 1. `jj untrack` can be implemented as removing paths from the tree
    object and then doing a reset of the working copy state.

 2. Importing Git HEAD when sharing the working copy with a Git repo.

This patch adds that functionality to `TreeState`.
2022-01-19 08:32:59 -08:00
Martin von Zweigbergk
9a640bfe13 working_copy: save TreeState later, just before releasing lock
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.
2022-01-19 08:32:59 -08:00
Martin von Zweigbergk
25d19e8a65 working_copy: start improving interface for mutations
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`.
2022-01-19 08:32:59 -08:00
Martin von Zweigbergk
2916dc3423 working_copy: make a &mut self argument not mutable 2022-01-19 08:32:51 -08:00
Martin von Zweigbergk
1fc19dbbaf working_copy: take initial commit in init() function
The working copy object knows the currently checked out commit ID. It
is set to `None` when the object is initialized. It is also set to
`None` when an existing working copy is loaded. In that case, it's
used only to facilitate lazy loading. However, that means that
`WorkingCopy::current_commit_id()` fails if the working copy has been
initalized but no checkout has been specified. I've never run into
that case, but it's ugly that it can happen. This patch fixes it by
having `WorkingCopy::init()` take a `CommitId`.
2022-01-17 14:12:55 -08:00
Martin von Zweigbergk
0fadac38d6 working_copy: remove current_commit() (leaving current_commit_id()
`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.
2022-01-15 17:11:56 -08:00
Martin von Zweigbergk
1f68de64d4 debug: upgrade Drop implementations to non-debug assert!
I think these remaining implementations of `Drop` are for types that
are infrequently dropped (unlike `Transaction`), so it be fine to be
more strict about them.
2021-12-01 10:32:11 -08:00
Martin von Zweigbergk
8cf5dd286a backend: make Vec inside CommitId non-public
The recent e5dd93cbf712, whose description says "cleanup: make Vec
inside CommitId etc. non-public", made all ID types in the `backend`
module *except* for `CommitId` non-public :P This patch makes
2021-11-19 23:19:00 -08:00
Martin von Zweigbergk
33b272f5fa working_copy: make some functions require mutable references
We use interior mutability for caching in `WorkingCopy`, but let's
still take mutable reference in the functions where the state change
is visible.
2021-11-17 10:15:33 -08:00
Martin von Zweigbergk
287602966e working_copy: add a method for untracking specified paths 2021-11-17 08:41:37 -08:00
Martin von Zweigbergk
e5dd93cbf7 cleanup: make Vec inside CommitId etc. non-public 2021-11-10 10:46:10 -08:00
Martin von Zweigbergk
ea82340654 working_copy: preserve conflicts in the working copy until markers are removed
I realized only recently that we can try to parse conflict markers in
files and leave them as conflicted if they haven't changed. If they
have changed and some conflict markers have been removed, we can even
update the conflict with that partial resolution.

This change teaches the working copy to write conflicts to the working
copy. It used to expect that the caller had already updated the tree
by materializing conflicts. With this change, we also start parsing
the conflict markers and leave the conflicts unresolved in the working
copy if the conflict markers remain.

There are some cases that we don't handle yet. For example, we don't
even try to set the executable bit correctly when we write
conflicts. OTOH, we didn't do that even before this change.

We still never actually write conflicts to the working copy (outside
of tests) because we currently materialize conflicts in
`MutRepo::check_out()`. I'll change that next.
2021-11-07 15:17:51 -08:00
Martin von Zweigbergk
856b219943 working_copy: if a file's recorded mtime is equal to the state's mtime, set to 0 early 2021-11-07 15:17:51 -08:00
Martin von Zweigbergk
033bfe7d5b working_copy: fix an incorrect comment about timestamps 2021-11-07 15:17:51 -08:00