Skip to content

Conversation

@scott2000
Copy link
Contributor

#1176

I split this PR off of #7692 because I think these changes could be reviewed separately to figure out the storage of conflict labels before we go into the specifics of adding the labels for each type of conflict and rendering them in conflict markers. I added some justification for this approach in the description of the first commit, but I'd be open to a different approach if reviewers think it would work better.

Checklist

If applicable:

  • I have updated CHANGELOG.md
  • I have updated the documentation (README.md, docs/, demos/)
  • I have updated the config schema (cli/src/config-schema.json)
  • I have added/updated tests to cover my changes

Adding this as a separate type will help maintain the invariant that
resolved merges cannot have labels. I added `Arc` since the labels will
often need to be cloned, such as in `MergedTree::sub_tree`,
`MergedTree::id`, and when reading and writing root trees.

I think that storing separate conflict labels for each term of the
conflict is the best approach for a couple reasons. Mainly, I think it
integrates well with the existing conflict algebra. For instance, a diff
of (A - B) and a diff of (B - C) can be easily combined to create a new
diff of (A - C), and if we associate a label with each term, then the
labels will also naturally be carried over as well. Also, I think it
would be simpler to implement than other approaches (such as storing
labels for diffs instead of terms), since conflict labels can re-use
existing logic from `Merge<T>`.

For simplicity, I also think we shouldn't allow mixing labeled terms and
unlabeled terms (i.e. if any term doesn't have a label, then we discard
all labels and leave the entire merge unlabeled). I think it could be
confusing to have conflicts where, for instance, one side says "rebase
destination" and another side only says "side #2" with no further
information. In cases like these, I think it's better to just fall back
to the old labels. In the future, I expect that most conflicts should
have labels (since we should eventually be adding labels everywhere
conflicts can happen).
Since two merged trees can now have the same contents but different
conflict labels, many places that previously compared `MergedTreeId` for
equality now need to ignore the conflict labels. To solve this, I added
a `MergedTreeId::has_changes` method to check whether the underlying
`Merge<TreeId>` has any changes.
To implement simplification of conflict labels, I decided to add more
functions such as `zip` and `unzip` to `Merge`. I think these functions
could be useful in other situations so I thought this was a nice
solution, but an alternative solution could be to make
`get_simplified_mapping` and `apply_simplified_mapping` public and
manually apply the same mapping to both merges.
The old method is renamed to `MergedTree::merge_unlabeled` to make it
easy to find unmigrated callers. The goal is that almost all callers
will eventually use `MergedTree::merge` to add labels, unless the
resulting tree is never visible to the user.
Conflict labels are stored in a separate header for backwards
compatibility.
If a conflicted file didn't change, but the conflict labels changed, we
will still need to re-materialize the file in the working copy to ensure
that the conflict labels are displayed properly.
@scott2000 scott2000 requested a review from a team as a code owner October 26, 2025 02:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant