Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 44 additions & 7 deletions crates/server/src/yas_git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1081,14 +1081,12 @@ fn run_query(
.map_err(native_error)?;
let records = match response {
yas_git::native::PatchResult::Text(data) => {
let object = endpoint_content_object(right)
let object = match endpoint_content_object(right)
.or_else(|| endpoint_content_object(left))
.ok_or_else(|| {
Error::new(
Status::Unsupported,
"text patch between mutable endpoints has no content object",
)
})?;
{
Some(object) => object,
None => null_object(object_algorithm)?,
};
let byte_len = data.len() as u64;
return Ok(query_page(
vec![QueryItem::Content(OwnedContent {
Expand Down Expand Up @@ -1896,6 +1894,20 @@ fn native_endpoint(endpoint: &wire::QueryEndpoint) -> Result<yas_git::native::En
})
}

/// Content object for a text patch between two mutable endpoints (INDEX,
/// WORKTREE, EMPTY): the null ID, as `git diff` prints for the worktree side.
fn null_object(object_algorithm: u8) -> Result<wire::ObjectId, Error> {
let len = match object_algorithm {
value if value == yas_wire::schema::git::OBJECT_SHA1 as u8 => 20,
value if value == yas_wire::schema::git::OBJECT_SHA256 as u8 => 32,
_ => return Err(Error::new(Status::Internal, "unknown Git object algorithm")),
};
Ok(wire::ObjectId {
algorithm: object_algorithm,
bytes: vec![0; len],
})
}

fn endpoint_content_object(endpoint: &wire::QueryEndpoint) -> Option<wire::ObjectId> {
match endpoint {
wire::QueryEndpoint::Commit(object)
Expand Down Expand Up @@ -2274,6 +2286,31 @@ mod tests {
.any(|record| matches!(record, QueryItem::Record(wire::QueryRecord::PatchRow(_))))
);

let text = run_query(
Some(&repository),
algorithm,
&query(wire::QueryBody::Patch {
left: wire::QueryEndpoint::Index,
right: wire::QueryEndpoint::Worktree,
path: None,
context_lines: 3,
rename_threshold: 50,
max_bytes: 1024 * 1024,
flags: yas_wire::schema::git::PATCH_TEXT as u16,
}),
None,
&cancel,
)
.unwrap();
match text.records.as_slice() {
[QueryItem::Content(OwnedContent { object, bytes, .. })] => {
assert!(object.bytes.iter().all(|byte| *byte == 0));
let text = String::from_utf8_lossy(bytes);
assert!(text.contains("-hello\n+hello native YAS\n"), "{text}");
}
other => panic!("unexpected index/worktree patch page: {other:?}"),
}

for body in [
wire::QueryBody::Index {
path: None,
Expand Down
5 changes: 4 additions & 1 deletion docs/design/git.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,10 @@ the tree via gix) with intraline span refinement on modified line pairs —
word- or character-granular, over raw or whitespace-normalized text, per
request flags; binary detection short-circuits to `BINARY`. The row
records are engine-agnostic by design, so a syntax-aware engine can
replace the alignment later, purely server-side.
replace the alignment later, purely server-side. A `PATCH_TEXT` answer's
content object is the right endpoint's object, else the left's; between two
mutable endpoints (INDEX, WORKTREE, EMPTY) it is the null object ID, as
`git diff` prints for the worktree side.

Nothing runs under the session mutex; Results and Events interleave with
Terminal, Surface, Media, and FS traffic through the per-session writer.
Expand Down
Loading