forked from mirrors/jj
backend: rename ConflictPart
to ConflictTerm
It took a while before I realized that conflicts could be modeled as simple algebraic expressions with positive and negative terms (they were modeled as recursive 3-way conflicts initially). We've been thinking of them that way for a while now, so let's make the `ConflictPart` name match that model.
This commit is contained in:
parent
e48ace56d1
commit
a87125d08b
10 changed files with 119 additions and 119 deletions
|
@ -152,7 +152,7 @@ content_hash! {
|
||||||
|
|
||||||
content_hash! {
|
content_hash! {
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct ConflictPart {
|
pub struct ConflictTerm {
|
||||||
// TODO: Store e.g. CommitId here too? Labels (theirs/ours/base)? Would those still be
|
// TODO: Store e.g. CommitId here too? Labels (theirs/ours/base)? Would those still be
|
||||||
// useful e.g. after rebasing this conflict?
|
// useful e.g. after rebasing this conflict?
|
||||||
pub value: TreeValue,
|
pub value: TreeValue,
|
||||||
|
@ -166,8 +166,8 @@ content_hash! {
|
||||||
// In a simple 3-way merge of B and C with merge base A, the conflict will be { add: [B, C],
|
// In a simple 3-way merge of B and C with merge base A, the conflict will be { add: [B, C],
|
||||||
// remove: [A] }. Also note that a conflict of the form { add: [A], remove: [] } is the
|
// remove: [A] }. Also note that a conflict of the form { add: [A], remove: [] } is the
|
||||||
// same as non-conflict A.
|
// same as non-conflict A.
|
||||||
pub removes: Vec<ConflictPart>,
|
pub removes: Vec<ConflictTerm>,
|
||||||
pub adds: Vec<ConflictPart>,
|
pub adds: Vec<ConflictTerm>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ use std::io::{Cursor, Write};
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::backend::{BackendResult, Conflict, ConflictId, ConflictPart, ObjectId, TreeValue};
|
use crate::backend::{BackendResult, Conflict, ConflictId, ConflictTerm, ObjectId, TreeValue};
|
||||||
use crate::diff::{find_line_ranges, Diff, DiffHunk};
|
use crate::diff::{find_line_ranges, Diff, DiffHunk};
|
||||||
use crate::files;
|
use crate::files;
|
||||||
use crate::files::{ConflictHunk, MergeHunk, MergeResult};
|
use crate::files::{ConflictHunk, MergeHunk, MergeResult};
|
||||||
|
@ -30,8 +30,8 @@ const CONFLICT_DIFF_LINE: &[u8] = b"%%%%%%%\n";
|
||||||
const CONFLICT_MINUS_LINE: &[u8] = b"-------\n";
|
const CONFLICT_MINUS_LINE: &[u8] = b"-------\n";
|
||||||
const CONFLICT_PLUS_LINE: &[u8] = b"+++++++\n";
|
const CONFLICT_PLUS_LINE: &[u8] = b"+++++++\n";
|
||||||
|
|
||||||
fn describe_conflict_part(part: &ConflictPart) -> String {
|
fn describe_conflict_term(term: &ConflictTerm) -> String {
|
||||||
match &part.value {
|
match &term.value {
|
||||||
TreeValue::File {
|
TreeValue::File {
|
||||||
id,
|
id,
|
||||||
executable: false,
|
executable: false,
|
||||||
|
@ -62,21 +62,21 @@ fn describe_conflict_part(part: &ConflictPart) -> String {
|
||||||
/// Give a summary description of a conflict's "adds" and "removes"
|
/// Give a summary description of a conflict's "adds" and "removes"
|
||||||
pub fn describe_conflict(conflict: &Conflict, file: &mut dyn Write) -> std::io::Result<()> {
|
pub fn describe_conflict(conflict: &Conflict, file: &mut dyn Write) -> std::io::Result<()> {
|
||||||
file.write_all(b"Conflict:\n")?;
|
file.write_all(b"Conflict:\n")?;
|
||||||
for part in &conflict.removes {
|
for term in &conflict.removes {
|
||||||
file.write_all(format!(" Removing {}\n", describe_conflict_part(part)).as_bytes())?;
|
file.write_all(format!(" Removing {}\n", describe_conflict_term(term)).as_bytes())?;
|
||||||
}
|
}
|
||||||
for part in &conflict.adds {
|
for term in &conflict.adds {
|
||||||
file.write_all(format!(" Adding {}\n", describe_conflict_part(part)).as_bytes())?;
|
file.write_all(format!(" Adding {}\n", describe_conflict_term(term)).as_bytes())?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file_parts(parts: &[ConflictPart]) -> Vec<&ConflictPart> {
|
fn file_terms(terms: &[ConflictTerm]) -> Vec<&ConflictTerm> {
|
||||||
parts
|
terms
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|part| {
|
.filter(|term| {
|
||||||
matches!(
|
matches!(
|
||||||
part.value,
|
term.value,
|
||||||
TreeValue::File {
|
TreeValue::File {
|
||||||
executable: false,
|
executable: false,
|
||||||
..
|
..
|
||||||
|
@ -86,11 +86,11 @@ fn file_parts(parts: &[ConflictPart]) -> Vec<&ConflictPart> {
|
||||||
.collect_vec()
|
.collect_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_file_contents(store: &Store, path: &RepoPath, part: &ConflictPart) -> Vec<u8> {
|
fn get_file_contents(store: &Store, path: &RepoPath, term: &ConflictTerm) -> Vec<u8> {
|
||||||
if let TreeValue::File {
|
if let TreeValue::File {
|
||||||
id,
|
id,
|
||||||
executable: false,
|
executable: false,
|
||||||
} = &part.value
|
} = &term.value
|
||||||
{
|
{
|
||||||
let mut content: Vec<u8> = vec![];
|
let mut content: Vec<u8> = vec![];
|
||||||
store
|
store
|
||||||
|
@ -100,7 +100,7 @@ fn get_file_contents(store: &Store, path: &RepoPath, part: &ConflictPart) -> Vec
|
||||||
.unwrap();
|
.unwrap();
|
||||||
content
|
content
|
||||||
} else {
|
} else {
|
||||||
panic!("unexpectedly got a non-file conflict part");
|
panic!("unexpectedly got a non-file conflict term");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@ pub fn materialize_conflict(
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
match extract_file_conflict_as_single_hunk(store, path, conflict) {
|
match extract_file_conflict_as_single_hunk(store, path, conflict) {
|
||||||
None => {
|
None => {
|
||||||
// Unless all parts are regular files, we can't do much better than to try to
|
// Unless all terms are regular files, we can't do much better than to try to
|
||||||
// describe the conflict.
|
// describe the conflict.
|
||||||
describe_conflict(conflict, output)
|
describe_conflict(conflict, output)
|
||||||
}
|
}
|
||||||
|
@ -144,24 +144,24 @@ pub fn materialize_conflict(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Only works if all parts of the conflict are regular, non-executable files
|
/// Only works if all terms of the conflict are regular, non-executable files
|
||||||
pub fn extract_file_conflict_as_single_hunk(
|
pub fn extract_file_conflict_as_single_hunk(
|
||||||
store: &Store,
|
store: &Store,
|
||||||
path: &RepoPath,
|
path: &RepoPath,
|
||||||
conflict: &Conflict,
|
conflict: &Conflict,
|
||||||
) -> Option<ConflictHunk> {
|
) -> Option<ConflictHunk> {
|
||||||
let file_adds = file_parts(&conflict.adds);
|
let file_adds = file_terms(&conflict.adds);
|
||||||
let file_removes = file_parts(&conflict.removes);
|
let file_removes = file_terms(&conflict.removes);
|
||||||
if file_adds.len() != conflict.adds.len() || file_removes.len() != conflict.removes.len() {
|
if file_adds.len() != conflict.adds.len() || file_removes.len() != conflict.removes.len() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let mut added_content = file_adds
|
let mut added_content = file_adds
|
||||||
.iter()
|
.iter()
|
||||||
.map(|part| get_file_contents(store, path, part))
|
.map(|term| get_file_contents(store, path, term))
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
let mut removed_content = file_removes
|
let mut removed_content = file_removes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|part| get_file_contents(store, path, part))
|
.map(|term| get_file_contents(store, path, term))
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
// If the conflict had removed the file on one side, we pretend that the file
|
// If the conflict had removed the file on one side, we pretend that the file
|
||||||
// was empty there.
|
// was empty there.
|
||||||
|
|
|
@ -24,7 +24,7 @@ use prost::Message;
|
||||||
|
|
||||||
use crate::backend::{
|
use crate::backend::{
|
||||||
make_root_commit, Backend, BackendError, BackendResult, ChangeId, Commit, CommitId, Conflict,
|
make_root_commit, Backend, BackendError, BackendResult, ChangeId, Commit, CommitId, Conflict,
|
||||||
ConflictId, ConflictPart, FileId, MillisSinceEpoch, ObjectId, Signature, SymlinkId, Timestamp,
|
ConflictId, ConflictTerm, FileId, MillisSinceEpoch, ObjectId, Signature, SymlinkId, Timestamp,
|
||||||
Tree, TreeId, TreeValue,
|
Tree, TreeId, TreeValue,
|
||||||
};
|
};
|
||||||
use crate::repo_path::{RepoPath, RepoPathComponent};
|
use crate::repo_path::{RepoPath, RepoPathComponent};
|
||||||
|
@ -370,15 +370,15 @@ impl Backend for GitBackend {
|
||||||
file.read_to_string(&mut data)?;
|
file.read_to_string(&mut data)?;
|
||||||
let json: serde_json::Value = serde_json::from_str(&data).unwrap();
|
let json: serde_json::Value = serde_json::from_str(&data).unwrap();
|
||||||
Ok(Conflict {
|
Ok(Conflict {
|
||||||
removes: conflict_part_list_from_json(json.get("removes").unwrap()),
|
removes: conflict_term_list_from_json(json.get("removes").unwrap()),
|
||||||
adds: conflict_part_list_from_json(json.get("adds").unwrap()),
|
adds: conflict_term_list_from_json(json.get("adds").unwrap()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_conflict(&self, _path: &RepoPath, conflict: &Conflict) -> BackendResult<ConflictId> {
|
fn write_conflict(&self, _path: &RepoPath, conflict: &Conflict) -> BackendResult<ConflictId> {
|
||||||
let json = serde_json::json!({
|
let json = serde_json::json!({
|
||||||
"removes": conflict_part_list_to_json(&conflict.removes),
|
"removes": conflict_term_list_to_json(&conflict.removes),
|
||||||
"adds": conflict_part_list_to_json(&conflict.adds),
|
"adds": conflict_term_list_to_json(&conflict.adds),
|
||||||
});
|
});
|
||||||
let json_string = json.to_string();
|
let json_string = json.to_string();
|
||||||
let bytes = json_string.as_bytes();
|
let bytes = json_string.as_bytes();
|
||||||
|
@ -548,27 +548,27 @@ impl Backend for GitBackend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn conflict_part_list_to_json(parts: &[ConflictPart]) -> serde_json::Value {
|
fn conflict_term_list_to_json(parts: &[ConflictTerm]) -> serde_json::Value {
|
||||||
serde_json::Value::Array(parts.iter().map(conflict_part_to_json).collect())
|
serde_json::Value::Array(parts.iter().map(conflict_term_to_json).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn conflict_part_list_from_json(json: &serde_json::Value) -> Vec<ConflictPart> {
|
fn conflict_term_list_from_json(json: &serde_json::Value) -> Vec<ConflictTerm> {
|
||||||
json.as_array()
|
json.as_array()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
.map(conflict_part_from_json)
|
.map(conflict_term_from_json)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn conflict_part_to_json(part: &ConflictPart) -> serde_json::Value {
|
fn conflict_term_to_json(part: &ConflictTerm) -> serde_json::Value {
|
||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
"value": tree_value_to_json(&part.value),
|
"value": tree_value_to_json(&part.value),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn conflict_part_from_json(json: &serde_json::Value) -> ConflictPart {
|
fn conflict_term_from_json(json: &serde_json::Value) -> ConflictTerm {
|
||||||
let json_value = json.get("value").unwrap();
|
let json_value = json.get("value").unwrap();
|
||||||
ConflictPart {
|
ConflictTerm {
|
||||||
value: tree_value_from_json(json_value),
|
value: tree_value_from_json(json_value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ use tempfile::{NamedTempFile, PersistError};
|
||||||
|
|
||||||
use crate::backend::{
|
use crate::backend::{
|
||||||
make_root_commit, Backend, BackendError, BackendResult, ChangeId, Commit, CommitId, Conflict,
|
make_root_commit, Backend, BackendError, BackendResult, ChangeId, Commit, CommitId, Conflict,
|
||||||
ConflictId, ConflictPart, FileId, MillisSinceEpoch, ObjectId, Signature, SymlinkId, Timestamp,
|
ConflictId, ConflictTerm, FileId, MillisSinceEpoch, ObjectId, Signature, SymlinkId, Timestamp,
|
||||||
Tree, TreeId, TreeValue,
|
Tree, TreeId, TreeValue,
|
||||||
};
|
};
|
||||||
use crate::content_hash::blake2b_hash;
|
use crate::content_hash::blake2b_hash;
|
||||||
|
@ -401,34 +401,34 @@ fn signature_from_proto(proto: crate::protos::store::commit::Signature) -> Signa
|
||||||
|
|
||||||
fn conflict_to_proto(conflict: &Conflict) -> crate::protos::store::Conflict {
|
fn conflict_to_proto(conflict: &Conflict) -> crate::protos::store::Conflict {
|
||||||
let mut proto = crate::protos::store::Conflict::default();
|
let mut proto = crate::protos::store::Conflict::default();
|
||||||
for part in &conflict.adds {
|
for term in &conflict.adds {
|
||||||
proto.adds.push(conflict_part_to_proto(part));
|
proto.adds.push(conflict_term_to_proto(term));
|
||||||
}
|
}
|
||||||
for part in &conflict.removes {
|
for term in &conflict.removes {
|
||||||
proto.removes.push(conflict_part_to_proto(part));
|
proto.removes.push(conflict_term_to_proto(term));
|
||||||
}
|
}
|
||||||
proto
|
proto
|
||||||
}
|
}
|
||||||
|
|
||||||
fn conflict_from_proto(proto: crate::protos::store::Conflict) -> Conflict {
|
fn conflict_from_proto(proto: crate::protos::store::Conflict) -> Conflict {
|
||||||
let mut conflict = Conflict::default();
|
let mut conflict = Conflict::default();
|
||||||
for part in proto.removes {
|
for term in proto.removes {
|
||||||
conflict.removes.push(conflict_part_from_proto(part))
|
conflict.removes.push(conflict_term_from_proto(term))
|
||||||
}
|
}
|
||||||
for part in proto.adds {
|
for term in proto.adds {
|
||||||
conflict.adds.push(conflict_part_from_proto(part))
|
conflict.adds.push(conflict_term_from_proto(term))
|
||||||
}
|
}
|
||||||
conflict
|
conflict
|
||||||
}
|
}
|
||||||
|
|
||||||
fn conflict_part_from_proto(proto: crate::protos::store::conflict::Part) -> ConflictPart {
|
fn conflict_term_from_proto(proto: crate::protos::store::conflict::Term) -> ConflictTerm {
|
||||||
ConflictPart {
|
ConflictTerm {
|
||||||
value: tree_value_from_proto(proto.content.unwrap()),
|
value: tree_value_from_proto(proto.content.unwrap()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn conflict_part_to_proto(part: &ConflictPart) -> crate::protos::store::conflict::Part {
|
fn conflict_term_to_proto(part: &ConflictTerm) -> crate::protos::store::conflict::Term {
|
||||||
crate::protos::store::conflict::Part {
|
crate::protos::store::conflict::Term {
|
||||||
content: Some(tree_value_to_proto(&part.value)),
|
content: Some(tree_value_to_proto(&part.value)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,10 +63,10 @@ message Commit {
|
||||||
}
|
}
|
||||||
|
|
||||||
message Conflict {
|
message Conflict {
|
||||||
message Part {
|
message Term {
|
||||||
TreeValue content = 1;
|
TreeValue content = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
repeated Part removes = 1;
|
repeated Term removes = 1;
|
||||||
repeated Part adds = 2;
|
repeated Term adds = 2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,15 +93,15 @@ pub mod commit {
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct Conflict {
|
pub struct Conflict {
|
||||||
#[prost(message, repeated, tag = "1")]
|
#[prost(message, repeated, tag = "1")]
|
||||||
pub removes: ::prost::alloc::vec::Vec<conflict::Part>,
|
pub removes: ::prost::alloc::vec::Vec<conflict::Term>,
|
||||||
#[prost(message, repeated, tag = "2")]
|
#[prost(message, repeated, tag = "2")]
|
||||||
pub adds: ::prost::alloc::vec::Vec<conflict::Part>,
|
pub adds: ::prost::alloc::vec::Vec<conflict::Term>,
|
||||||
}
|
}
|
||||||
/// Nested message and enum types in `Conflict`.
|
/// Nested message and enum types in `Conflict`.
|
||||||
pub mod conflict {
|
pub mod conflict {
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct Part {
|
pub struct Term {
|
||||||
#[prost(message, optional, tag = "1")]
|
#[prost(message, optional, tag = "1")]
|
||||||
pub content: ::core::option::Option<super::TreeValue>,
|
pub content: ::core::option::Option<super::TreeValue>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ use itertools::Itertools;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::backend::{
|
use crate::backend::{
|
||||||
BackendError, Conflict, ConflictId, ConflictPart, FileId, ObjectId,
|
BackendError, Conflict, ConflictId, ConflictTerm, FileId, ObjectId,
|
||||||
TreeEntriesNonRecursiveIterator, TreeEntry, TreeId, TreeValue,
|
TreeEntriesNonRecursiveIterator, TreeEntry, TreeId, TreeValue,
|
||||||
};
|
};
|
||||||
use crate::files::MergeResult;
|
use crate::files::MergeResult;
|
||||||
|
@ -611,17 +611,17 @@ fn merge_tree_value(
|
||||||
// resolved state, the absence of a state, or a conflicted state.
|
// resolved state, the absence of a state, or a conflicted state.
|
||||||
let mut conflict = Conflict::default();
|
let mut conflict = Conflict::default();
|
||||||
if let Some(base) = maybe_base {
|
if let Some(base) = maybe_base {
|
||||||
conflict.removes.push(ConflictPart {
|
conflict.removes.push(ConflictTerm {
|
||||||
value: base.clone(),
|
value: base.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if let Some(side1) = maybe_side1 {
|
if let Some(side1) = maybe_side1 {
|
||||||
conflict.adds.push(ConflictPart {
|
conflict.adds.push(ConflictTerm {
|
||||||
value: side1.clone(),
|
value: side1.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if let Some(side2) = maybe_side2 {
|
if let Some(side2) = maybe_side2 {
|
||||||
conflict.adds.push(ConflictPart {
|
conflict.adds.push(ConflictTerm {
|
||||||
value: side2.clone(),
|
value: side2.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -667,8 +667,8 @@ fn try_resolve_file_conflict(
|
||||||
let mut regular_delta = 0;
|
let mut regular_delta = 0;
|
||||||
let mut removed_file_ids = vec![];
|
let mut removed_file_ids = vec![];
|
||||||
let mut added_file_ids = vec![];
|
let mut added_file_ids = vec![];
|
||||||
for part in &conflict.removes {
|
for term in &conflict.removes {
|
||||||
match &part.value {
|
match &term.value {
|
||||||
TreeValue::File { id, executable } => {
|
TreeValue::File { id, executable } => {
|
||||||
if *executable {
|
if *executable {
|
||||||
exec_delta -= 1;
|
exec_delta -= 1;
|
||||||
|
@ -682,8 +682,8 @@ fn try_resolve_file_conflict(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for part in &conflict.adds {
|
for term in &conflict.adds {
|
||||||
match &part.value {
|
match &term.value {
|
||||||
TreeValue::File { id, executable } => {
|
TreeValue::File { id, executable } => {
|
||||||
if *executable {
|
if *executable {
|
||||||
exec_delta += 1;
|
exec_delta += 1;
|
||||||
|
@ -739,19 +739,19 @@ fn try_resolve_file_conflict(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn conflict_part_to_conflict(
|
fn conflict_term_to_conflict(
|
||||||
store: &Store,
|
store: &Store,
|
||||||
path: &RepoPath,
|
path: &RepoPath,
|
||||||
part: &ConflictPart,
|
term: &ConflictTerm,
|
||||||
) -> Result<Conflict, BackendError> {
|
) -> Result<Conflict, BackendError> {
|
||||||
match &part.value {
|
match &term.value {
|
||||||
TreeValue::Conflict(id) => {
|
TreeValue::Conflict(id) => {
|
||||||
let conflict = store.read_conflict(path, id)?;
|
let conflict = store.read_conflict(path, id)?;
|
||||||
Ok(conflict)
|
Ok(conflict)
|
||||||
}
|
}
|
||||||
other => Ok(Conflict {
|
other => Ok(Conflict {
|
||||||
removes: vec![],
|
removes: vec![],
|
||||||
adds: vec![ConflictPart {
|
adds: vec![ConflictTerm {
|
||||||
value: other.clone(),
|
value: other.clone(),
|
||||||
}],
|
}],
|
||||||
}),
|
}),
|
||||||
|
@ -797,27 +797,27 @@ fn simplify_conflict(
|
||||||
// First expand any diffs with nested conflicts.
|
// First expand any diffs with nested conflicts.
|
||||||
let mut new_removes = vec![];
|
let mut new_removes = vec![];
|
||||||
let mut new_adds = vec![];
|
let mut new_adds = vec![];
|
||||||
for part in &conflict.adds {
|
for term in &conflict.adds {
|
||||||
match part.value {
|
match term.value {
|
||||||
TreeValue::Conflict(_) => {
|
TreeValue::Conflict(_) => {
|
||||||
let conflict = conflict_part_to_conflict(store, path, part)?;
|
let conflict = conflict_term_to_conflict(store, path, term)?;
|
||||||
new_removes.extend_from_slice(&conflict.removes);
|
new_removes.extend_from_slice(&conflict.removes);
|
||||||
new_adds.extend_from_slice(&conflict.adds);
|
new_adds.extend_from_slice(&conflict.adds);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
new_adds.push(part.clone());
|
new_adds.push(term.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for part in &conflict.removes {
|
for term in &conflict.removes {
|
||||||
match part.value {
|
match term.value {
|
||||||
TreeValue::Conflict(_) => {
|
TreeValue::Conflict(_) => {
|
||||||
let conflict = conflict_part_to_conflict(store, path, part)?;
|
let conflict = conflict_term_to_conflict(store, path, term)?;
|
||||||
new_removes.extend_from_slice(&conflict.adds);
|
new_removes.extend_from_slice(&conflict.adds);
|
||||||
new_adds.extend_from_slice(&conflict.removes);
|
new_adds.extend_from_slice(&conflict.removes);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
new_removes.push(part.clone());
|
new_removes.push(term.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use jujutsu_lib::backend::{Conflict, ConflictPart, FileId, TreeValue};
|
use jujutsu_lib::backend::{Conflict, ConflictTerm, FileId, TreeValue};
|
||||||
use jujutsu_lib::conflicts::{materialize_conflict, parse_conflict, update_conflict_from_content};
|
use jujutsu_lib::conflicts::{materialize_conflict, parse_conflict, update_conflict_from_content};
|
||||||
use jujutsu_lib::files::{ConflictHunk, MergeHunk};
|
use jujutsu_lib::files::{ConflictHunk, MergeHunk};
|
||||||
use jujutsu_lib::repo::Repo;
|
use jujutsu_lib::repo::Repo;
|
||||||
|
@ -20,8 +20,8 @@ use jujutsu_lib::repo_path::RepoPath;
|
||||||
use jujutsu_lib::store::Store;
|
use jujutsu_lib::store::Store;
|
||||||
use testutils::TestRepo;
|
use testutils::TestRepo;
|
||||||
|
|
||||||
fn file_conflict_part(file_id: &FileId) -> ConflictPart {
|
fn file_conflict_term(file_id: &FileId) -> ConflictTerm {
|
||||||
ConflictPart {
|
ConflictTerm {
|
||||||
value: TreeValue::File {
|
value: TreeValue::File {
|
||||||
id: file_id.clone(),
|
id: file_id.clone(),
|
||||||
executable: false,
|
executable: false,
|
||||||
|
@ -69,8 +69,8 @@ line 5
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut conflict = Conflict {
|
let mut conflict = Conflict {
|
||||||
removes: vec![file_conflict_part(&base_id)],
|
removes: vec![file_conflict_term(&base_id)],
|
||||||
adds: vec![file_conflict_part(&left_id), file_conflict_part(&right_id)],
|
adds: vec![file_conflict_term(&left_id), file_conflict_term(&right_id)],
|
||||||
};
|
};
|
||||||
insta::assert_snapshot!(
|
insta::assert_snapshot!(
|
||||||
&materialize_conflict_string(store, &path, &conflict),
|
&materialize_conflict_string(store, &path, &conflict),
|
||||||
|
@ -150,10 +150,10 @@ line 5
|
||||||
|
|
||||||
// left modifies a line, right deletes the same line.
|
// left modifies a line, right deletes the same line.
|
||||||
let conflict = Conflict {
|
let conflict = Conflict {
|
||||||
removes: vec![file_conflict_part(&base_id)],
|
removes: vec![file_conflict_term(&base_id)],
|
||||||
adds: vec![
|
adds: vec![
|
||||||
file_conflict_part(&modified_id),
|
file_conflict_term(&modified_id),
|
||||||
file_conflict_part(&deleted_id),
|
file_conflict_term(&deleted_id),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
insta::assert_snapshot!(&materialize_conflict_string(store, &path, &conflict), @r###"
|
insta::assert_snapshot!(&materialize_conflict_string(store, &path, &conflict), @r###"
|
||||||
|
@ -172,10 +172,10 @@ line 5
|
||||||
|
|
||||||
// right modifies a line, left deletes the same line.
|
// right modifies a line, left deletes the same line.
|
||||||
let conflict = Conflict {
|
let conflict = Conflict {
|
||||||
removes: vec![file_conflict_part(&base_id)],
|
removes: vec![file_conflict_term(&base_id)],
|
||||||
adds: vec![
|
adds: vec![
|
||||||
file_conflict_part(&deleted_id),
|
file_conflict_term(&deleted_id),
|
||||||
file_conflict_part(&modified_id),
|
file_conflict_term(&modified_id),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
insta::assert_snapshot!(&materialize_conflict_string(store, &path, &conflict), @r###"
|
insta::assert_snapshot!(&materialize_conflict_string(store, &path, &conflict), @r###"
|
||||||
|
@ -194,8 +194,8 @@ line 5
|
||||||
|
|
||||||
// modify/delete conflict at the file level
|
// modify/delete conflict at the file level
|
||||||
let conflict = Conflict {
|
let conflict = Conflict {
|
||||||
removes: vec![file_conflict_part(&base_id)],
|
removes: vec![file_conflict_term(&base_id)],
|
||||||
adds: vec![file_conflict_part(&modified_id)],
|
adds: vec![file_conflict_term(&modified_id)],
|
||||||
};
|
};
|
||||||
insta::assert_snapshot!(&materialize_conflict_string(store, &path, &conflict), @r###"
|
insta::assert_snapshot!(&materialize_conflict_string(store, &path, &conflict), @r###"
|
||||||
<<<<<<<
|
<<<<<<<
|
||||||
|
@ -381,10 +381,10 @@ fn test_update_conflict_from_content() {
|
||||||
let left_file_id = testutils::write_file(store, &path, "left 1\nline 2\nleft 3\n");
|
let left_file_id = testutils::write_file(store, &path, "left 1\nline 2\nleft 3\n");
|
||||||
let right_file_id = testutils::write_file(store, &path, "right 1\nline 2\nright 3\n");
|
let right_file_id = testutils::write_file(store, &path, "right 1\nline 2\nright 3\n");
|
||||||
let conflict = Conflict {
|
let conflict = Conflict {
|
||||||
removes: vec![file_conflict_part(&base_file_id)],
|
removes: vec![file_conflict_term(&base_file_id)],
|
||||||
adds: vec![
|
adds: vec![
|
||||||
file_conflict_part(&left_file_id),
|
file_conflict_term(&left_file_id),
|
||||||
file_conflict_part(&right_file_id),
|
file_conflict_term(&right_file_id),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
let conflict_id = store.write_conflict(&path, &conflict).unwrap();
|
let conflict_id = store.write_conflict(&path, &conflict).unwrap();
|
||||||
|
@ -424,10 +424,10 @@ fn test_update_conflict_from_content() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
new_conflict,
|
new_conflict,
|
||||||
Conflict {
|
Conflict {
|
||||||
removes: vec![file_conflict_part(&new_base_file_id)],
|
removes: vec![file_conflict_term(&new_base_file_id)],
|
||||||
adds: vec![
|
adds: vec![
|
||||||
file_conflict_part(&new_left_file_id),
|
file_conflict_term(&new_left_file_id),
|
||||||
file_conflict_part(&new_right_file_id)
|
file_conflict_term(&new_right_file_id)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use jujutsu_lib::backend::{ConflictPart, TreeValue};
|
use jujutsu_lib::backend::{ConflictTerm, TreeValue};
|
||||||
use jujutsu_lib::repo::Repo;
|
use jujutsu_lib::repo::Repo;
|
||||||
use jujutsu_lib::repo_path::{RepoPath, RepoPathComponent};
|
use jujutsu_lib::repo_path::{RepoPath, RepoPathComponent};
|
||||||
use jujutsu_lib::rewrite::rebase_commit;
|
use jujutsu_lib::rewrite::rebase_commit;
|
||||||
|
@ -126,10 +126,10 @@ fn test_same_type(use_git: bool) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
conflict.adds,
|
conflict.adds,
|
||||||
vec![
|
vec![
|
||||||
ConflictPart {
|
ConflictTerm {
|
||||||
value: side1_tree.value(&component).cloned().unwrap()
|
value: side1_tree.value(&component).cloned().unwrap()
|
||||||
},
|
},
|
||||||
ConflictPart {
|
ConflictTerm {
|
||||||
value: side2_tree.value(&component).cloned().unwrap()
|
value: side2_tree.value(&component).cloned().unwrap()
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -146,13 +146,13 @@ fn test_same_type(use_git: bool) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
conflict.removes,
|
conflict.removes,
|
||||||
vec![ConflictPart {
|
vec![ConflictTerm {
|
||||||
value: base_tree.value(&component).cloned().unwrap()
|
value: base_tree.value(&component).cloned().unwrap()
|
||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
conflict.adds,
|
conflict.adds,
|
||||||
vec![ConflictPart {
|
vec![ConflictTerm {
|
||||||
value: side2_tree.value(&component).cloned().unwrap()
|
value: side2_tree.value(&component).cloned().unwrap()
|
||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
|
@ -167,13 +167,13 @@ fn test_same_type(use_git: bool) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
conflict.removes,
|
conflict.removes,
|
||||||
vec![ConflictPart {
|
vec![ConflictTerm {
|
||||||
value: base_tree.value(&component).cloned().unwrap()
|
value: base_tree.value(&component).cloned().unwrap()
|
||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
conflict.adds,
|
conflict.adds,
|
||||||
vec![ConflictPart {
|
vec![ConflictTerm {
|
||||||
value: side1_tree.value(&component).cloned().unwrap()
|
value: side1_tree.value(&component).cloned().unwrap()
|
||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
|
@ -188,17 +188,17 @@ fn test_same_type(use_git: bool) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
conflict.removes,
|
conflict.removes,
|
||||||
vec![ConflictPart {
|
vec![ConflictTerm {
|
||||||
value: base_tree.value(&component).cloned().unwrap()
|
value: base_tree.value(&component).cloned().unwrap()
|
||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
conflict.adds,
|
conflict.adds,
|
||||||
vec![
|
vec![
|
||||||
ConflictPart {
|
ConflictTerm {
|
||||||
value: side1_tree.value(&component).cloned().unwrap()
|
value: side1_tree.value(&component).cloned().unwrap()
|
||||||
},
|
},
|
||||||
ConflictPart {
|
ConflictTerm {
|
||||||
value: side2_tree.value(&component).cloned().unwrap()
|
value: side2_tree.value(&component).cloned().unwrap()
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -406,17 +406,17 @@ fn test_types(use_git: bool) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
conflict.removes,
|
conflict.removes,
|
||||||
vec![ConflictPart {
|
vec![ConflictTerm {
|
||||||
value: base_tree.value(&component).cloned().unwrap()
|
value: base_tree.value(&component).cloned().unwrap()
|
||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
conflict.adds,
|
conflict.adds,
|
||||||
vec![
|
vec![
|
||||||
ConflictPart {
|
ConflictTerm {
|
||||||
value: side1_tree.value(&component).cloned().unwrap()
|
value: side1_tree.value(&component).cloned().unwrap()
|
||||||
},
|
},
|
||||||
ConflictPart {
|
ConflictTerm {
|
||||||
value: side2_tree.value(&component).cloned().unwrap()
|
value: side2_tree.value(&component).cloned().unwrap()
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -432,17 +432,17 @@ fn test_types(use_git: bool) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
conflict.removes,
|
conflict.removes,
|
||||||
vec![ConflictPart {
|
vec![ConflictTerm {
|
||||||
value: base_tree.value(&component).cloned().unwrap()
|
value: base_tree.value(&component).cloned().unwrap()
|
||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
conflict.adds,
|
conflict.adds,
|
||||||
vec![
|
vec![
|
||||||
ConflictPart {
|
ConflictTerm {
|
||||||
value: side1_tree.value(&component).cloned().unwrap()
|
value: side1_tree.value(&component).cloned().unwrap()
|
||||||
},
|
},
|
||||||
ConflictPart {
|
ConflictTerm {
|
||||||
value: side2_tree.value(&component).cloned().unwrap()
|
value: side2_tree.value(&component).cloned().unwrap()
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -507,17 +507,17 @@ fn test_simplify_conflict(use_git: bool) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
conflict.removes,
|
conflict.removes,
|
||||||
vec![ConflictPart {
|
vec![ConflictTerm {
|
||||||
value: base_tree.value(&component).cloned().unwrap()
|
value: base_tree.value(&component).cloned().unwrap()
|
||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
conflict.adds,
|
conflict.adds,
|
||||||
vec![
|
vec![
|
||||||
ConflictPart {
|
ConflictTerm {
|
||||||
value: branch_tree.value(&component).cloned().unwrap()
|
value: branch_tree.value(&component).cloned().unwrap()
|
||||||
},
|
},
|
||||||
ConflictPart {
|
ConflictTerm {
|
||||||
value: upstream2_tree.value(&component).cloned().unwrap()
|
value: upstream2_tree.value(&component).cloned().unwrap()
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -531,17 +531,17 @@ fn test_simplify_conflict(use_git: bool) {
|
||||||
let conflict = store.read_conflict(&path, id).unwrap();
|
let conflict = store.read_conflict(&path, id).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
conflict.removes,
|
conflict.removes,
|
||||||
vec![ConflictPart {
|
vec![ConflictTerm {
|
||||||
value: base_tree.value(&component).cloned().unwrap()
|
value: base_tree.value(&component).cloned().unwrap()
|
||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
conflict.adds,
|
conflict.adds,
|
||||||
vec![
|
vec![
|
||||||
ConflictPart {
|
ConflictTerm {
|
||||||
value: upstream2_tree.value(&component).cloned().unwrap()
|
value: upstream2_tree.value(&component).cloned().unwrap()
|
||||||
},
|
},
|
||||||
ConflictPart {
|
ConflictTerm {
|
||||||
value: branch_tree.value(&component).cloned().unwrap()
|
value: branch_tree.value(&component).cloned().unwrap()
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
@ -21,7 +21,7 @@ use std::os::unix::net::UnixListener;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use jujutsu_lib::backend::{Conflict, ConflictPart, TreeValue};
|
use jujutsu_lib::backend::{Conflict, ConflictTerm, TreeValue};
|
||||||
use jujutsu_lib::gitignore::GitIgnoreFile;
|
use jujutsu_lib::gitignore::GitIgnoreFile;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use jujutsu_lib::op_store::OperationId;
|
use jujutsu_lib::op_store::OperationId;
|
||||||
|
@ -121,20 +121,20 @@ fn test_checkout_file_transitions(use_git: bool) {
|
||||||
let left_file_id = testutils::write_file(store, path, "left file contents");
|
let left_file_id = testutils::write_file(store, path, "left file contents");
|
||||||
let right_file_id = testutils::write_file(store, path, "right file contents");
|
let right_file_id = testutils::write_file(store, path, "right file contents");
|
||||||
let conflict = Conflict {
|
let conflict = Conflict {
|
||||||
removes: vec![ConflictPart {
|
removes: vec![ConflictTerm {
|
||||||
value: TreeValue::File {
|
value: TreeValue::File {
|
||||||
id: base_file_id,
|
id: base_file_id,
|
||||||
executable: false,
|
executable: false,
|
||||||
},
|
},
|
||||||
}],
|
}],
|
||||||
adds: vec![
|
adds: vec![
|
||||||
ConflictPart {
|
ConflictTerm {
|
||||||
value: TreeValue::File {
|
value: TreeValue::File {
|
||||||
id: left_file_id,
|
id: left_file_id,
|
||||||
executable: false,
|
executable: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ConflictPart {
|
ConflictTerm {
|
||||||
value: TreeValue::File {
|
value: TreeValue::File {
|
||||||
id: right_file_id,
|
id: right_file_id,
|
||||||
executable: false,
|
executable: false,
|
||||||
|
|
Loading…
Reference in a new issue