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

464 commits

Author SHA1 Message Date
Martin von Zweigbergk
f846112f80 cleanup: remove an unnecessary reference-taking noticed by Clippy 2021-11-14 12:47:14 -08:00
Martin von Zweigbergk
30845c1f3b cleanup: fix formatting regression 2021-11-14 12:46:38 -08:00
Martin von Zweigbergk
ced252f766 cleanup: replace some as_slice() by & 2021-11-10 10:55:58 -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
27107637f2 git: fix regression in pruning of remote refs
A while ago, I replaced a call to git2-rs's `Remote::fetch()` by calls
to `Remote::download()` and `Remote::update_tips()`. The function is
documented to be a convenience for those function, but it turns out
that the pruning of deleted remote refs is a separate call
(`Remote::prune()`), so we need to call that too.
2021-11-07 16:02:18 -08:00
Martin von Zweigbergk
bdea3ae31a git: don't pass remote credentials callback to Remote::update_tips()
AFAICT, the credentials callback is not used by
`Remote::update_tips()`.
2021-11-07 15:57:19 -08:00
Martin von Zweigbergk
e901a4e66b repo: don't materialize conflicts on checkout
Since the working copy can now handle conflicts, we don't need to
materialize conflicts when checking out a commit.

Before this patch, we used to create a new commit on top whenever we
checked out a commit with conflicts. That new commit was intended just
for resolving the conflicts. The typical workflow was the resolve the
conflicts and then amend. To use the same workflow after this patch,
one needs to explicitly create a new commit on top with `jj new` after
checking out a commit with conflict.
2021-11-07 15:17:51 -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
cea6061f3d conflicts: add a function for parsing a materialized conflict
I initially made the working copy materialize conflicts in its
`check_out()` method. Then I changed it later (exactly a year ago, on
Halloween of 2020, actually) so that the working copy expected
conflicts to already have been materalized, which happens in
`MutableRepo::check_out`().

I think my reasoning then was that the file system cannot represent a
conflict. While it's true that the file system itself doesn't have
information to know whether a file represents a conflict, we can
record that ourselves. We already record whether a file is executable
or not and then preserve that if we're on a file system that isn't
able to record it. It's not that different to do the same for
conflicts if we're on a file system that doesn't understand conflicts
(i.e. all file systems).

The plan is to have the working copy remember whether a file
represents a conflict. When we check if it has changed, we parse the
file, including conflict markers, and recreate the conflict from
it. We should be able to do that losslessly (and we should adjust
formats to make it possible if we find cases where it's not).

Having the working copy preserve conflict states has several
advantages:

 * Because conflicts are not materialized in the working copy, you can
   rebase the conflicted commit and the working copy without causing
   more conflicts (that's currently a UX bug I run into every now and
   then).

 * If you don't change anything in the working copy, it will be
   unchanged compared to its parent, which means we'll automatically
   abandon it if you update away from it.

 * The user can choose to resolve only some of the conflicts in a file
   and squash those in, and it'll work they way you'd hope.

 * It should make it easier to implement support for external merge
   tools (#18) without having them treat the working copy differently.

This patch prepares for that work by adding support for parsing
materialized conflicts.
2021-11-07 15:17:51 -08:00
Martin von Zweigbergk
f6839a4ceb files: implement Debug for MergeResult, display byte vector as string 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
Martin von Zweigbergk
9ac643ed1a working_copy: rename confusingly named read_time field 2021-11-07 15:17:51 -08:00
Martin von Zweigbergk
baa403b41e working_copy: clarify code for preserving current file state
On Windows, we preserve the executable bit. I plan to also teach the
working copy to preserve conflict state. This refactoring prepares for
that by simplifying how we preserve parts of the current file state.
2021-11-07 15:17:51 -08:00
Martin von Zweigbergk
7c12014513 working_copy: extract a function for updating the file state and tree if dirty 2021-11-07 15:17:51 -08:00
Martin von Zweigbergk
730c442462 working_copy: move file_state() off of TreeState
The function doesn't use `self`, so let's move it off of
`TreeState`. That way we can call it even while holding a mutable
reference.
2021-11-07 15:17:51 -08:00
Martin von Zweigbergk
f86ffbd47b working_copy: take advantage of DirEntry::path() 2021-11-07 15:17:51 -08:00
Martin von Zweigbergk
5dce6e9884 working_copy: avoid using file_state() for getting mtime of tree_state file
I want to change the signature of `file_state()` and this caller is
different from the others and only needs the mtime.
2021-11-07 15:17:51 -08:00
Martin von Zweigbergk
efe7316354 working_copy: make FileType variants more similar to TreeValue variants
This change doesn't make much difference yet, but I think it will
enable further improvements.
2021-11-07 15:17:51 -08:00
Martin von Zweigbergk
bf61e9cf3e working_copy: don't stat ignored files 2021-11-07 15:17:51 -08:00
Martin von Zweigbergk
76c58b3949 tree_builder: rename repo() to more accurate store() 2021-11-07 15:17:51 -08:00
Martin von Zweigbergk
acf3798abc revsets: allow underscore in identifiers 2021-11-07 07:55:52 -08:00
Martin von Zweigbergk
9abee0096f conflicts: propagate errors from materialize_conflict()
All current callers pass in a buffer, so it should never fail, but the
function itself can't know that.
2021-10-24 23:02:00 -07:00
Martin von Zweigbergk
0f46c2453e revset graph: fix formatting I missed in recent commit 2021-10-23 20:53:41 -07:00
Martin von Zweigbergk
b4a0e513dd revset graph: make order of edges stable
While working on demos, I noticed that `jj log` output in the
octocat/Hello-World repo was unstable: sometimes the first parent of
the merge was on the left and sometimes it was on the right. This
patch fixes that by sorting the edges by position in the index just
before returning them. It seems that most applications would want
stable output so I put it in the `RevsetGraphIterator` rather than
doing at the call site in the CLI. I ordered them with the reverse
index position rather than forward because it seemed to make the
graphs in the git.git repo slight nicer, with the left-most edge going
between subsequent releases.

There performance difference is within the noise level.
2021-10-23 20:33:59 -07:00
Martin von Zweigbergk
33bf6ce1d5 view: don't force commits pointed to by refs to be current heads
If you rewrite a commit that's also available on some remote, you'll
currently see both the old version and the new version in the view,
which means they're divergent. They're not logically divergent (the
rewritten version should replace the old version), so this is a UX
bug. I think it indicates that the set of current heads should be
redefined to be the *desired* heads. That's also what I had suspected
in the TODO removed by this change.  I think another indication that
we should hide the old heads even if they have e.g. a remote branch
pointing to them is that we don't want them to be rebased if we
rewrite an ancestor.

So that's what I decided to do: let the view's heads be the desired
heads. The user can still define revsets for showing non-current
commits pointed to by e.g. remote branches.
2021-10-23 09:15:05 -07:00
Martin von Zweigbergk
c0ae4b16e8 trees: try to resolve file conflicts after simplifying conflicts
This fixes a bug I've run into somewhat frequently. What happens is
that if you have a conflict on top of another conflict and you resolve
the conflict in the bottom commit, we just simplify the `Conflict`
object in the second commit, but we don't try to resolve the new
conflict. That shows up as an unexpected "conflict" in `jj log`
output, and when you check out the commit, there are actually no
conflicts, so you can just `jj squash` right away.

This patch fixes that bug. It also teaches the code to work with more
than 3 parts in the merge, so if there's a 5-way conflict, for
example, we still try to resolve it if possible.
2021-10-22 09:20:50 -07:00
Martin von Zweigbergk
f1e2124a64 trees: make simplify_conflict() always return a Conflict
The interface is much cleaner this way. The function no longer makes
any writes to the store.
2021-10-20 22:07:42 -07:00
Martin von Zweigbergk
2e4dc019d9 git: don't update public heads for now 2021-10-20 14:23:58 -07:00
Martin von Zweigbergk
23c3e02134 stacked_table: don't expect a certain listing order from the file system 2021-10-20 13:53:32 -07:00
Martin von Zweigbergk
c260fea811 GitBackend: move extra metadata from Git notes to stacked-table storage
Git notes (at least as implemented by libgit2) quickly gets really
slow, as noted in issue #7. This patch replaces it by a custom storage
format.

I tested the performance in the git.git repo with just a few hundred
annotated commits (~450, I think) and no sharding. I listed the first
~2900 commits there using `jj log --no-graph -r ,,v1.0.0 -T 'author
"\n"' | wc -l`. That took about 882ms. After this patch, it dropped to
108ms.

I did a similar test in this repo with 12700 annotated commits and
sharding, listing all visible commits. That took 142ms before this
patch (the sharding helps a lot!) and 55ms after.

Closes #3.
Closes #7.
2021-10-20 13:22:59 -07:00
Martin von Zweigbergk
deddd90762 stacked_table: add caching of tables
We don't want to re-read the whole table(s) every time we read extra
metadata for a commit (which is the immediate use-case I'm aiming for
in #7)..
2021-10-20 13:22:36 -07:00
Martin von Zweigbergk
85773cf81f stacked_table: add a generic store based on the stacked-table format
The new store works the same way as the `OpHeadsStore`. It keeps track
of the current head file(s) by recording their names in a
directory. When a write happens, it adds the new head and then removes
the old head. There will be generally be a single head at a time. The
only exception is when there's been concurrent operations (locally, or
remotely, in the case of a distributed file system). When there are
multiple heads files, they are automatically merged. No guarantee is
given about which value wins if the key exists in several heads; the
store is meant to be used for data that's immutable once written. As
long as different keys are written, this is a CRDT. That makes it fit
for solving both #3 and #7.
2021-10-20 13:21:12 -07:00
Martin von Zweigbergk
e86d266e6b stacked_table: add a file format for stacked, sorted tables
I'm trying to replace the Git backend's use of Git notes for storing
metadata (#7). This patch adds a file format that I hope can be used
for that. It's a simple generic format for storing fixed-size keys and
associated variable-size values. The keys are stored in sorted
order. Each key is followed by an offset to the value. The offset is
relative to the first value. All values are concatenated after each
other. I suppose it's a bit like Git's pack files but lacking both
delta-encoding and compression.

Each file can also have a parent pointer (just like the index files
have), so we don't have to rewrite the whole file each time. As with
the index files, the new format squashes a file into its parent if it
contains more than half the number of entries of the parent. The code
is also based on `index.rs`.

Perhaps we can alo replace the default operation storage with this
format. Maybe also the native local backend's storage. We'll need
delta-encoding and compression soon then.
2021-10-20 13:19:32 -07:00
Martin von Zweigbergk
d8795b9ae7 store: move logic for initialization of GitBackend to that type 2021-10-18 08:49:22 -07:00
Martin von Zweigbergk
e8f44fb470 index: remove predecessor information from the index
Since we removed the evolution state in commit 4c4e436f38, we no
longer use the predecessor information in the index, so let's remove
it.
2021-10-17 09:13:31 -07:00
Martin von Zweigbergk
0354b8721b index: transparently reindex if the index is corrupt
I'm about to change the index format (to remove predecessor
information), which will break the format. Let's prepare for that by
having `IndexStore` reindex the repo if it fails to read the index..
2021-10-17 09:13:31 -07:00
Martin von Zweigbergk
6722a79e27 repo: fix crash on upgrade of repo backed by external Git repo
If the repo is backed by Git repo that's not inside `.jj/`, then
there's no `.jj/git/` directory to move into `.jj/store/`.
2021-10-14 16:41:57 -07:00
Martin von Zweigbergk
97b48635f6 repo: move Backend initialization to Store
This is just a cleanup. Now only `Store` knows how to tell which
backend to use.
2021-10-13 23:55:16 -07:00
Martin von Zweigbergk
1b6efdc3f8 repo: make .jj/store a directory also in Git-backed repos
I think this is just cleaner, and it gives us room to put other
store-related data in the `.jj/store/` directory. I may want to use
that place for writing the metadata we currently write in Git notes
(#7).
2021-10-13 23:42:25 -07:00
Martin von Zweigbergk
dba72af857 repo: move creation of directories .jj/* directories closer and earlier 2021-10-13 22:49:37 -07:00
Martin von Zweigbergk
c0a26f7642 conflicts: work around rust-lang/rust#89716 2021-10-13 13:41:09 -07:00
Martin von Zweigbergk
b4b64eb7e5 conflicts: add tests of conflict materialization 2021-10-13 13:40:25 -07:00
Martin von Zweigbergk
d8c0282873 revset: make head() accept candidate set to find heads within
With this change, you can do e.g. `heads(remote_branches())`. That
should currently be the same as `public_heads()`, except that we don't
yet remove public heads when remote branches have been updated. Having
this support should be generally useful, but I may use it in the short
term specifically for depending less on the public heads, until I get
around to keeping them up to date.
2021-10-13 09:41:20 -07:00
Martin von Zweigbergk
aebdd4e8fd revset: add all() function 2021-10-13 09:28:11 -07:00
Martin von Zweigbergk
5874d5abfb revset: add none() function 2021-10-13 09:24:08 -07:00
Martin von Zweigbergk
8bbfe9731e cleanup: let newer Clippy fix a few things it found 2021-10-13 08:27:44 -07:00
Martin von Zweigbergk
94408f01e0 revset: extract a function for creating a revset from commit ids
This is just to avoid some repetition.
2021-10-10 12:44:35 -07:00
Martin von Zweigbergk
8465a578be revset: split out a new remote_branches() from branches()
It seems useful to be able to use `remote_branches()` in revsets
e.g. for filtering out commits known on remotes.
2021-10-10 12:24:48 -07:00
Martin von Zweigbergk
e52c902d3a diff: compact adjacent unchanged regions also when using for_tokenizer()
I noticed while working on support for unified diffs (#33) that
`Diff::for_tokenizer(..., &find_line_ranges)` would return a
`DiffHunk::Matching` for each matching line instead of a single
`DiffHunk::Matching` for all the matching lines. That's different from
what you get from `Diff::default_refinement()` and seems less
convenient to work with.
2021-10-10 00:00:06 -07:00