Although rare, it would be very confusing to a user if they encountered
a conflict with a `-------` marker while using the "diff" style. This
addresses a TODO in the test.
`MergeOptions` isn't currently used directly in either module but it's
more closely related to `tree_merge`. It's possible we will want to
pass it into `ConflictIterator`, however.
I have no idea if we can change the default, but maybe we can conditionally
disable the same-change rule to get rid of hacks in squash_commits(), etc.?
resolve_file_executable() doesn't respect the Store configuration. I'm not sure
if that's the right choice, but it seemed better to accept duplicated change
than falling back to executable=false.
#6369
I'll add same-change rule parameter to disable A+(A-B)=A resolution. Since tree
merging involves content merging, file-level parameters should also be included
in the "tree" merge options. We could add a nested options type, but I don't
think we'll add an option that applies only to tree-level merging. So this patch
unifies these options types as MergeOptions.
This option affects emptiness of commits, which means indexed changed files can
become stale on configuration change. This problem can also be said for changes
in the diff algorithms, so I don't think we need a logic to invalidate index on
config change.
I have this patch for months, and it seems working good at least for Rust
sources.
Closes#17
I'll add word-by-word merging flag. Since we need to run auto-merge with the
configured options, these options are carried by the Store for now. When we add
a "jj resolve" flag to run auto-merge with different configuration, we might
have to parameterize the options passed in to MergedTree::resolve().
It seemed odd that we had to pass ConflictMarkerStyle to snapshot functions.
Suppose materialize/parse functions aren't lossy, we can compare hunks in
Vec<Merge<BString>> form. This should also be robust for configuration changes.
test_materialize_parse_roundtrip_different_markers() is simplified as there
should no longer be "pair" of marker styles.
I'm thinking of adding an option to do word-by-word merging, which will be pass
down to files::merge_hunks() through materialize_merge_result*(). Since tree
merging should also respect this option, it will be carried by the Store, and
copied to ConflictMaterializeOptions.
The current comment uses `[noeol]`, which can be difficult to
understand. In this commit, the brackets are changed to parentheses to
make it clear that there is no semantic meaning to the comment, and the
wording is changed to be more clear to the user.
Git doesn't show this information in their "diff3" style either, and it
looks a bit strange having two different bracketed sections of text next
to each other, so I think it would be better to remove it.
A missing newline would cause the following conflict markers to become
invalid when materializing the conflict, so we add a newline to prevent
this from happening. When parsing, if we parsed a conflict at the end of
the file which ended in a newline, we can check whether the file
actually did end in a newline, and then remove the newline if it didn't.
File conflicts have been simplified during materialization since 0.19.0,
so it should be safe to remove support for unsimplified conflicts now.
This will make implementing the next commit easier, since it won't have
to support unsimplified conflicts.
Storing the conflict marker length in the working copy makes conflict
parsing more consistent, and it allows us to parse valid conflict hunks
even if the user left some invalid conflict markers in the file while
resolving the conflicts.
If a file contains lines which look like conflict markers, then we need
to make the real conflict markers longer so that the materialized
conflicts can be parsed unambiguously.
When parsing the conflict, we require that the conflict markers are at
least as long as the materialized conflict markers based on the current
tree. This can lead to some unintuitive edge cases which will be solved
in the next commit.
For instance, if we have a file explaining the differences between
Jujutsu's conflict markers and Git's conflict markers, it could produce
a conflict with long markers like this:
```
<<<<<<<<<<< Conflict 1 of 1
%%%%%%%%%%% Changes from base to side #1
Jujutsu uses different conflict markers than Git, which just shows the
-sides of a conflict without a diff.
+sides of a conflict without a diff:
+
+<<<<<<<
+left
+|||||||
+base
+=======
+right
+>>>>>>>
+++++++++++ Contents of side #2
Jujutsu uses different conflict markers than Git:
<<<<<<<
%%%%%%%
-base
+left
+++++++
right
>>>>>>>
>>>>>>>>>>> Conflict 1 of 1 ends
```
We should support these options for "git" conflict marker style as well,
since Git actually does support producing longer conflict markers in
some cases through .gitattributes:
https://git-scm.com/docs/gitattributes#_conflict_marker_size
We may also want to support passing the conflict marker length to merge
tools as well in the future, since Git supports a "%L" parameter to pass
the conflict marker length to merge drivers:
https://git-scm.com/docs/gitattributes#_defining_a_custom_merge_driver
Adds a new "git" conflict marker style option. This option matches Git's
"diff3" conflict style, allowing these conflicts to be parsed by some
external tools that don't support JJ-style conflicts. If a conflict has
more than 2 sides, then it falls back to the similar "snapshot" conflict
marker style.
The conflict parsing code now supports parsing Git-style conflict
markers in addition to the normal JJ-style conflict markers, regardless
of the conflict marker style setting. This has the benefit of allowing
the user to switch the conflict marker style while they already have
conflicts checked out, and their old conflicts will still be parsed
correctly.
Example of "git" conflict markers:
```
<<<<<<< Side #1 (Conflict 1 of 1)
fn example(word: String) {
println!("word is {word}");
||||||| Base
fn example(w: String) {
println!("word is {w}");
=======
fn example(w: &str) {
println!("word is {w}");
>>>>>>> Side #2 (Conflict 1 of 1 ends)
}
```
Adds a new "snapshot" conflict marker style which returns a series of
snapshots, similar to Git's "diff3" conflict style. The "snapshot"
option uses a subset of the conflict hunk headers as the "diff" option
(it just doesn't use "%%%%%%%"), meaning that the two options are
trivially compatible with each other (i.e. a file materialized with
"snapshot" can be parsed with "diff" and vice versa).
Example of "snapshot" conflict markers:
```
<<<<<<< Conflict 1 of 1
+++++++ Contents of side #1
fn example(word: String) {
println!("word is {word}");
------- Contents of base
fn example(w: String) {
println!("word is {w}");
+++++++ Contents of side #2
fn example(w: &str) {
println!("word is {w}");
>>>>>>> Conflict 1 of 1 ends
}
```
Adds a new "ui.conflict-marker-style" config option. The "diff" option
is the default jj-style conflict markers with a snapshot and a series of
diffs to apply to the snapshot. New conflict marker style options will
be added in later commits.
The majority of the changes in this commit are from passing the config
option down to the code that materializes the conflicts.
Example of "diff" conflict markers:
```
<<<<<<< Conflict 1 of 1
+++++++ Contents of side #1
fn example(word: String) {
println!("word is {word}");
%%%%%%% Changes from base to side #2
-fn example(w: String) {
+fn example(w: &str) {
println!("word is {w}");
>>>>>>> Conflict 1 of 1 ends
}
```
Some editors strip trailing whitespace on save, which breaks any diffs
which have context lines, since the parsing function expects them to
start with a space. There's no visual difference between " \n" and "\n",
so it seems reasonable to accept both.
Currently, conflict markers ending in CRLF line endings aren't allowed.
I don't see any reason why we should reject them, since some
editors/tools might produce CRLF automatically on Windows when saving
files, which would break the conflicts otherwise.
This follows up on https://github.com/martinvonz/jj/pull/3459 and adds a
label to the closing delimeter of each conflict, e.g. "Conflict 1 of 3
ends".
I didn't initially put any label at the ending delimeter since the
starting delimeter is already marked with "Conflict 1 of 3". However,
I'm now realizing that when I resolve conflicts, I usually go from top
to bottom. The first thing I do is delete the starting conflict
delimeter. It is when I get to the *end* of the conflict that I wonder
whether there are any more conflicts left in the file.
For example,
```
<<<<<<< Conflict 1 of 3
+++++++ Contents of side #1
left 3.1
left 3.2
left 3.3
%%%%%%% Changes from base to side #2
-line 3
+right 3.1
>>>>>>>
```
or
```
<<<<<<< Conflict 1 of 1
%%%%%%% Changes from base to side #1
-line 3
+right 3.1
+++++++ Contents of side #2
left 3.1
left 3.2
left 3.3
>>>>>>>
```
Currently, there is no way to disable these, this is TODO for a future
PR. Other TODOs for future PRs: make these labels configurable. After
that, we could support a `diff3/git`-like conflict format as well, in
principle.
Counting conflicts helps with knowing whether you fixed all the
conflicts while you are in the editor.
While labeling "side #1", etc, does not tell you the commit id or
description as requested in #1176, I still think it's an improvement.
Most importantly, I hope this will make `jj`'s conflict format less
scary-looking for new users.
I've used this for a bit, and I like it. Without the labels, I would see
that the two conflicts have a different order of conflict markers, but I
wouldn't be able to remember what that means. For longer diffs, it can
be tricky for me to quickly tell that it's a diff as opposed to one of
the sides. This also creates some hope of being able to navigate a
conflict with more than 2 sides.
Another not-so-secret goal for this is explained in
https://github.com/martinvonz/jj/pull/3109#issuecomment-2014140627. The
idea is a little weird, but I *think* it could be helpful, and I'd like
to experiment with it.
The format is 7 characters of the separator followed by a space and arbitrary
text, followed by a newline. Separator followed by a newline is also allowed.
E.g.:
<<<<<<< Random text
%%%%%%% Random text
line 2
-line 3
+left
line 4
+++++++ Random text
right
%%%%%%% Random text
line 2
+forward
line 3
line 4
>>>>>>> Random text
This commit only allows reading such conflicts.
I considered allowing longer separators (`<<<<<<<<<<<<<< Random text`), but we
wouldn't currently write them, so let's be strict for now.
7 characters if they are followed by a space and arbitrary text
Apart from (IMO) looking nicer, this will also sidestep the potential problem
that if the file contains actual jj conflict markers (`>>>>>>>` in the beginning
of a line, for example), jj would currently have trouble materializing and
subsequently parsing conflicts in the file if it actually became conflicted.
I'll demo this bug in either this or a subsequent PR. It's the kind of bug that
sounds serious in theory but might never cause a problem in practice.
After this PR, only `docs/tutorial.md` has a conflict marker that's not indented.
There's only one there, so hopefully it won't be too much of a pain to deal with.
I also indented other strings in `test_conflicts.rs`. IMO, this looks nice and
more consistent with the `insta::assert_snapshot` output. I didn't spend the
time to do the same for `test_resolve_command`.
We didn't have any tests with negative snapshots (after a `-------`
line). I initially thought we couldn't produce such conflict markers
anymore. I'm not sure we want to render conflicts like the one in the
test like this. I don't think I intended for `add_index` in the code
to be able to be two steps ahead of the remove. Maybe we should
rewrite the algorithm to not do that and thus never produce negative
snapshots.
This enables cheap str-to-RepoPath cast, which is useful when sorting and
filtering a large Vec<(String, _)> list by using matcher for example. It
will also eliminate temporary allocation by repo_path.parent().
During the transition to using more async code, I keep running into
https://github.com/rust-lang/futures-rs/issues/2090. Right now, I want
to convert `MergedTree::diff()` into a `Stream`. I don't want to
update all call sites at once, so instead I'm adding a
`MergedTree::diff_stream()` method, which just wraps
`MergedTree::diff()` in a `Stream. However, since the iterator is
synchronous, it needs to block on the async `Backend::read_tree()`
calls. If we then also block on the `Stream` in the CLI, we run into
the panic.
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.