|
|
@@ -25,75 +25,116 @@ using ::testing::Matcher;
|
|
|
using ::testing::MatchesRegex;
|
|
|
using ::testing::StrEq;
|
|
|
|
|
|
-// Processes conflict markers, including tracking of whether code is within a
|
|
|
-// conflict marker. Returns true if the line is consumed.
|
|
|
+// Represents the different kinds of version-control conflict markers that are
|
|
|
+// relevant for the autoupdater. One key concern here is the distinction between
|
|
|
+// "snapshot" and "diff" conflict regions. Snapshot regions are the more
|
|
|
+// traditional kind, where the entire region between two markers represents the
|
|
|
+// exact state of a region of the underlying file at some snapshot (e.g. the
|
|
|
+// base commit or one of the conflicting commits). Diff regions are
|
|
|
+// produced by jj. They show the diff between the base and one side of the
|
|
|
+// conflict, using a prefix character on each line: '+' indicates an added line,
|
|
|
+// '-' indicates a removed line, and ' ' indicates an unchanged line. Note that
|
|
|
+// a single conflict may contain both snapshot and diff regions.
|
|
|
+//
|
|
|
+// See https://docs.jj-vcs.dev/latest/conflicts/ for more information.
|
|
|
+enum class MarkerKind {
|
|
|
+ // Represents a line that is not a conflict marker.
|
|
|
+ None,
|
|
|
+ // Marks the start of a conflict, and potentially a snapshot region.
|
|
|
+ Start,
|
|
|
+ // Marks the end of a conflict.
|
|
|
+ End,
|
|
|
+ // Marks the start of a snapshot region.
|
|
|
+ Snapshot,
|
|
|
+ // Marks the start of a diff region.
|
|
|
+ Diff
|
|
|
+};
|
|
|
+
|
|
|
+// Processes conflict markers, including tracking the previous conflict marker.
|
|
|
+// Returns true if the line is consumed.
|
|
|
static auto TryConsumeConflictMarker(bool running_autoupdate,
|
|
|
llvm::StringRef line,
|
|
|
llvm::StringRef line_trimmed,
|
|
|
- bool& inside_conflict_marker)
|
|
|
+ MarkerKind& previous_marker)
|
|
|
-> ErrorOr<bool> {
|
|
|
- bool is_start = line.starts_with("<<<<<<<");
|
|
|
- bool is_end = line.starts_with(">>>>>>>");
|
|
|
- bool is_middle =
|
|
|
- // git internal conflict markers ("merge" and "diff3" style).
|
|
|
- line.starts_with("=======") ||
|
|
|
- line.starts_with("|||||||")
|
|
|
- // jj internal conflict markers ("snapshot" style).
|
|
|
- || line.starts_with("+++++++") || line.starts_with("-------");
|
|
|
- // jj internal conflict marker ("diff" style)
|
|
|
- bool is_jj_diff = line.starts_with("%%%%%%%");
|
|
|
+ MarkerKind new_marker;
|
|
|
+ if (line.starts_with("<<<<<<<")) {
|
|
|
+ new_marker = MarkerKind::Start;
|
|
|
+ } else if (line.starts_with(">>>>>>>")) {
|
|
|
+ new_marker = MarkerKind::End;
|
|
|
+ } else if (line.starts_with("=======") || line.starts_with("|||||||") ||
|
|
|
+ line.starts_with("+++++++") || line.starts_with("-------")) {
|
|
|
+ // git uses "=======" and "|||||||" to mark boundaries between conflict
|
|
|
+ // regions (which are always snapshots). jj uses "+++++++" and "-------" to
|
|
|
+ // mark the start of different kinds of snapshot regions.
|
|
|
+ new_marker = MarkerKind::Snapshot;
|
|
|
+ } else if (line.starts_with("%%%%%%%") || line.starts_with(R"(\\\\\\\)")) {
|
|
|
+ // jj uses "%%%%%%%" to mark the start of a diff region, and "\\\\\\\" to
|
|
|
+ // add a second line to a "%%%%%%%" marker for formatting purposes.
|
|
|
+ new_marker = MarkerKind::Diff;
|
|
|
+ } else {
|
|
|
+ new_marker = MarkerKind::None;
|
|
|
+ }
|
|
|
|
|
|
// When running the test, any conflict marker is an error.
|
|
|
- if (!running_autoupdate && (is_start || is_middle || is_end || is_jj_diff)) {
|
|
|
+ if (!running_autoupdate && (new_marker != MarkerKind::None)) {
|
|
|
return ErrorBuilder() << "Conflict marker found:\n" << line;
|
|
|
}
|
|
|
|
|
|
- if (is_jj_diff && running_autoupdate) {
|
|
|
- // TODO: Add support for JJ's diff-style conflict markers.
|
|
|
- return ErrorBuilder()
|
|
|
- << "Found jj \"diff\" style conflict marker."
|
|
|
- " Autoupdate only supports \"snapshot\" style conflict markers."
|
|
|
- " To switch, use `jj config set --repo ui.conflict-marker-style"
|
|
|
- " \"snapshot\"`, and then run `jj new` (or `jj edit`) again to "
|
|
|
- " materialize the new style. For more details, see: "
|
|
|
- "https://docs.jj-vcs.dev/latest/conflicts/";
|
|
|
- }
|
|
|
-
|
|
|
- // Autoupdate tracks conflict markers for context, and will discard
|
|
|
- // conflicting lines when it can autoupdate them.
|
|
|
- if (inside_conflict_marker) {
|
|
|
- if (is_start) {
|
|
|
- return ErrorBuilder() << "Unexpected conflict marker inside conflict:\n"
|
|
|
- << line;
|
|
|
- }
|
|
|
- if (is_middle) {
|
|
|
+ bool inside_conflict_marker = [&] {
|
|
|
+ switch (previous_marker) {
|
|
|
+ case MarkerKind::None:
|
|
|
+ case MarkerKind::End:
|
|
|
+ return false;
|
|
|
+ case MarkerKind::Start:
|
|
|
+ case MarkerKind::Snapshot:
|
|
|
+ case MarkerKind::Diff:
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }();
|
|
|
+
|
|
|
+ switch (new_marker) {
|
|
|
+ case MarkerKind::End:
|
|
|
+ case MarkerKind::Snapshot:
|
|
|
+ case MarkerKind::Diff:
|
|
|
+ if (!inside_conflict_marker) {
|
|
|
+ return ErrorBuilder()
|
|
|
+ << "Unexpected conflict marker outside conflict:\n"
|
|
|
+ << line;
|
|
|
+ }
|
|
|
+ previous_marker = new_marker;
|
|
|
return true;
|
|
|
- }
|
|
|
- if (is_end) {
|
|
|
- inside_conflict_marker = false;
|
|
|
+ case MarkerKind::Start:
|
|
|
+ if (inside_conflict_marker) {
|
|
|
+ return ErrorBuilder() << "Unexpected conflict marker inside conflict:\n"
|
|
|
+ << line;
|
|
|
+ }
|
|
|
+ previous_marker = new_marker;
|
|
|
return true;
|
|
|
- }
|
|
|
+ case MarkerKind::None:
|
|
|
+ if (!inside_conflict_marker) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- // Look for CHECK and TIP lines, which can be discarded.
|
|
|
- if (line_trimmed.starts_with("// CHECK:STDOUT:") ||
|
|
|
- line_trimmed.starts_with("// CHECK:STDERR:") ||
|
|
|
- line_trimmed.starts_with("// TIP:")) {
|
|
|
- return true;
|
|
|
- }
|
|
|
+ if (previous_marker == MarkerKind::Diff) {
|
|
|
+ if (!line.consume_front(" ") && !line.consume_front("+") &&
|
|
|
+ !line.consume_front("-")) {
|
|
|
+ return ErrorBuilder() << "Line inside diff-style conflict doesn't "
|
|
|
+ "start with '+', '-', or ' ':\n"
|
|
|
+ << line;
|
|
|
+ }
|
|
|
+ line_trimmed = line.ltrim();
|
|
|
+ }
|
|
|
|
|
|
- return ErrorBuilder()
|
|
|
- << "Autoupdate can't discard non-CHECK lines inside conflicts:\n"
|
|
|
- << line;
|
|
|
- } else {
|
|
|
- if (is_start) {
|
|
|
- inside_conflict_marker = true;
|
|
|
- return true;
|
|
|
- }
|
|
|
- if (is_middle || is_end) {
|
|
|
- return ErrorBuilder() << "Unexpected conflict marker outside conflict:\n"
|
|
|
- << line;
|
|
|
- }
|
|
|
- return false;
|
|
|
+ // Look for CHECK and TIP lines, which can be discarded.
|
|
|
+ if (line_trimmed.starts_with("// CHECK:STDOUT:") ||
|
|
|
+ line_trimmed.starts_with("// CHECK:STDERR:") ||
|
|
|
+ line_trimmed.starts_with("// TIP:")) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return ErrorBuilder() << "Autoupdate can't discard non-CHECK lines "
|
|
|
+ "inside conflicts:\n";
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -718,7 +759,7 @@ static auto ProcessFileContent(llvm::StringRef filename,
|
|
|
|
|
|
// When autoupdating, we track whether we're inside conflict markers.
|
|
|
// Otherwise conflict markers are errors.
|
|
|
- bool inside_conflict_marker = false;
|
|
|
+ auto previous_conflict_marker = MarkerKind::None;
|
|
|
|
|
|
SplitState split_state;
|
|
|
|
|
|
@@ -732,7 +773,7 @@ static auto ProcessFileContent(llvm::StringRef filename,
|
|
|
CARBON_ASSIGN_OR_RETURN(
|
|
|
is_consumed,
|
|
|
TryConsumeConflictMarker(running_autoupdate, line, line_trimmed,
|
|
|
- inside_conflict_marker));
|
|
|
+ previous_conflict_marker));
|
|
|
if (is_consumed) {
|
|
|
continue;
|
|
|
}
|