Skip to content

fix(mercury): parse commit from bytes lost gpgsig#1430

Merged
genedna merged 1 commit into
gitmono-dev:mainfrom
benjamin-747:main
Sep 10, 2025
Merged

fix(mercury): parse commit from bytes lost gpgsig#1430
genedna merged 1 commit into
gitmono-dev:mainfrom
benjamin-747:main

Conversation

@benjamin-747

Copy link
Copy Markdown
Collaborator

No description provided.

@vercel

vercel Bot commented Sep 10, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
mega Ready Ready Preview Comment Sep 10, 2025 8:09am

@benjamin-747

Copy link
Copy Markdown
Collaborator Author

回退代码到之前的稳定版本

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a parsing issue where the GPG signature field was being lost when parsing commit objects from bytes. The previous implementation incorrectly handled the commit format by skipping GPG signatures and other fields between headers.

Key changes:

  • Replaced line-by-line header parsing with sequential field extraction
  • Modified parsing logic to preserve GPG signatures and other fields in the commit message
  • Added test case to verify GPG signature preservation

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +195 to 197
let message = unsafe {
String::from_utf8_unchecked(commit[commit.find_byte(0x0a).unwrap() + 1..].to_vec())
};

Copilot AI Sep 10, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using unsafe and from_utf8_unchecked is risky here. Git commit data could contain invalid UTF-8 sequences. Consider using String::from_utf8_lossy or proper UTF-8 validation instead.

Suggested change
let message = unsafe {
String::from_utf8_unchecked(commit[commit.find_byte(0x0a).unwrap() + 1..].to_vec())
};
let message = String::from_utf8_lossy(
&commit[commit.find_byte(0x0a).unwrap() + 1..]
).to_string();

Copilot uses AI. Check for mistakes.
Comment on lines +152 to 197
let tree_end = commit.find_byte(0x0a).unwrap();
let tree_id: SHA1 = SHA1::from_str(
String::from_utf8(commit[5..tree_end].to_owned()) // 5 is the length of "tree "
.unwrap()
.as_str(),
)
.unwrap();
let binding = commit[tree_end + 1..].to_vec(); // Move past the tree id
commit = &binding;

// Find the blank line that separates headers from the message
if let Some(pos) = headers.find(b"\n\n") {
message_start = pos + 2;
headers = &headers[..pos];
} else {
// If no blank line, the whole data is headers, no message
}
// Find the parent commit ids and remove them from the data
let author_begin = commit.find("author").unwrap();
// Find all parent commit ids
// The parent commit ids are all the lines that start with "parent "
// We can use find_iter to find all occurrences of "parent "
// and then extract the SHA1 hashes from them.
let parent_commit_ids: Vec<SHA1> = commit[..author_begin]
.find_iter("parent")
.map(|parent| {
let parent_end = commit[parent..].find_byte(0x0a).unwrap();
SHA1::from_str(
// 7 is the length of "parent "
String::from_utf8(commit[parent + 7..parent + parent_end].to_owned())
.unwrap()
.as_str(),
)
.unwrap()
})
.collect();
let binding = commit[author_begin..].to_vec();
commit = &binding;

let mut tree_id: Option<SHA1> = None;
let mut parent_commit_ids: Vec<SHA1> = Vec::new();
let mut author: Option<Signature> = None;
let mut committer: Option<Signature> = None;
// Find the author and committer and remove them from the data
// 0x0a is the newline character
let author =
Signature::from_data(commit[..commit.find_byte(0x0a).unwrap()].to_vec()).unwrap();

for line in headers.lines() {
if let Some(tree_str) = line.strip_prefix(b"tree ") {
tree_id = Some(
SHA1::from_str(tree_str.to_str().map_err(|e| {
GitError::InvalidCommit(format!("Invalid UTF-8 in tree SHA: {}", e))
})?)
.map_err(|e| GitError::InvalidCommit(format!("Invalid tree SHA: {}", e)))?,
);
} else if let Some(parent_str) = line.strip_prefix(b"parent ") {
parent_commit_ids.push(
SHA1::from_str(parent_str.to_str().map_err(|e| {
GitError::InvalidCommit(format!("Invalid UTF-8 in parent SHA: {}", e))
})?)
.map_err(|e| GitError::InvalidCommit(format!("Invalid parent SHA: {}", e)))?,
);
} else if line.starts_with(b"author ") {
author = Some(Signature::from_data(line.to_vec()).map_err(|e| {
GitError::InvalidCommit(format!("Invalid author signature: {}", e))
})?);
} else if line.starts_with(b"committer ") {
committer = Some(Signature::from_data(line.to_vec()).map_err(|e| {
GitError::InvalidCommit(format!("Invalid committer signature: {}", e))
})?);
}
}
let binding = commit[commit.find_byte(0x0a).unwrap() + 1..].to_vec();
commit = &binding;
let committer =
Signature::from_data(commit[..commit.find_byte(0x0a).unwrap()].to_vec()).unwrap();

let message = if message_start > 0 {
String::from_utf8_lossy(&data[message_start..]).to_string()
} else {
String::new()
// The rest is the message
let message = unsafe {
String::from_utf8_unchecked(commit[commit.find_byte(0x0a).unwrap() + 1..].to_vec())
};

Copilot AI Sep 10, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple calls to unwrap() throughout the parsing logic could cause panics on malformed commit data. The original code had proper error handling. Consider returning appropriate GitError::InvalidCommit variants instead of panicking.

Copilot uses AI. Check for mistakes.
Comment on lines +152 to 197
let tree_end = commit.find_byte(0x0a).unwrap();
let tree_id: SHA1 = SHA1::from_str(
String::from_utf8(commit[5..tree_end].to_owned()) // 5 is the length of "tree "
.unwrap()
.as_str(),
)
.unwrap();
let binding = commit[tree_end + 1..].to_vec(); // Move past the tree id
commit = &binding;

// Find the blank line that separates headers from the message
if let Some(pos) = headers.find(b"\n\n") {
message_start = pos + 2;
headers = &headers[..pos];
} else {
// If no blank line, the whole data is headers, no message
}
// Find the parent commit ids and remove them from the data
let author_begin = commit.find("author").unwrap();
// Find all parent commit ids
// The parent commit ids are all the lines that start with "parent "
// We can use find_iter to find all occurrences of "parent "
// and then extract the SHA1 hashes from them.
let parent_commit_ids: Vec<SHA1> = commit[..author_begin]
.find_iter("parent")
.map(|parent| {
let parent_end = commit[parent..].find_byte(0x0a).unwrap();
SHA1::from_str(
// 7 is the length of "parent "
String::from_utf8(commit[parent + 7..parent + parent_end].to_owned())
.unwrap()
.as_str(),
)
.unwrap()
})
.collect();
let binding = commit[author_begin..].to_vec();
commit = &binding;

let mut tree_id: Option<SHA1> = None;
let mut parent_commit_ids: Vec<SHA1> = Vec::new();
let mut author: Option<Signature> = None;
let mut committer: Option<Signature> = None;
// Find the author and committer and remove them from the data
// 0x0a is the newline character
let author =
Signature::from_data(commit[..commit.find_byte(0x0a).unwrap()].to_vec()).unwrap();

for line in headers.lines() {
if let Some(tree_str) = line.strip_prefix(b"tree ") {
tree_id = Some(
SHA1::from_str(tree_str.to_str().map_err(|e| {
GitError::InvalidCommit(format!("Invalid UTF-8 in tree SHA: {}", e))
})?)
.map_err(|e| GitError::InvalidCommit(format!("Invalid tree SHA: {}", e)))?,
);
} else if let Some(parent_str) = line.strip_prefix(b"parent ") {
parent_commit_ids.push(
SHA1::from_str(parent_str.to_str().map_err(|e| {
GitError::InvalidCommit(format!("Invalid UTF-8 in parent SHA: {}", e))
})?)
.map_err(|e| GitError::InvalidCommit(format!("Invalid parent SHA: {}", e)))?,
);
} else if line.starts_with(b"author ") {
author = Some(Signature::from_data(line.to_vec()).map_err(|e| {
GitError::InvalidCommit(format!("Invalid author signature: {}", e))
})?);
} else if line.starts_with(b"committer ") {
committer = Some(Signature::from_data(line.to_vec()).map_err(|e| {
GitError::InvalidCommit(format!("Invalid committer signature: {}", e))
})?);
}
}
let binding = commit[commit.find_byte(0x0a).unwrap() + 1..].to_vec();
commit = &binding;
let committer =
Signature::from_data(commit[..commit.find_byte(0x0a).unwrap()].to_vec()).unwrap();

let message = if message_start > 0 {
String::from_utf8_lossy(&data[message_start..]).to_string()
} else {
String::new()
// The rest is the message
let message = unsafe {
String::from_utf8_unchecked(commit[commit.find_byte(0x0a).unwrap() + 1..].to_vec())
};

Copilot AI Sep 10, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple calls to unwrap() throughout the parsing logic could cause panics on malformed commit data. The original code had proper error handling. Consider returning appropriate GitError::InvalidCommit variants instead of panicking.

Copilot uses AI. Check for mistakes.
Comment on lines +152 to 197
let tree_end = commit.find_byte(0x0a).unwrap();
let tree_id: SHA1 = SHA1::from_str(
String::from_utf8(commit[5..tree_end].to_owned()) // 5 is the length of "tree "
.unwrap()
.as_str(),
)
.unwrap();
let binding = commit[tree_end + 1..].to_vec(); // Move past the tree id
commit = &binding;

// Find the blank line that separates headers from the message
if let Some(pos) = headers.find(b"\n\n") {
message_start = pos + 2;
headers = &headers[..pos];
} else {
// If no blank line, the whole data is headers, no message
}
// Find the parent commit ids and remove them from the data
let author_begin = commit.find("author").unwrap();
// Find all parent commit ids
// The parent commit ids are all the lines that start with "parent "
// We can use find_iter to find all occurrences of "parent "
// and then extract the SHA1 hashes from them.
let parent_commit_ids: Vec<SHA1> = commit[..author_begin]
.find_iter("parent")
.map(|parent| {
let parent_end = commit[parent..].find_byte(0x0a).unwrap();
SHA1::from_str(
// 7 is the length of "parent "
String::from_utf8(commit[parent + 7..parent + parent_end].to_owned())
.unwrap()
.as_str(),
)
.unwrap()
})
.collect();
let binding = commit[author_begin..].to_vec();
commit = &binding;

let mut tree_id: Option<SHA1> = None;
let mut parent_commit_ids: Vec<SHA1> = Vec::new();
let mut author: Option<Signature> = None;
let mut committer: Option<Signature> = None;
// Find the author and committer and remove them from the data
// 0x0a is the newline character
let author =
Signature::from_data(commit[..commit.find_byte(0x0a).unwrap()].to_vec()).unwrap();

for line in headers.lines() {
if let Some(tree_str) = line.strip_prefix(b"tree ") {
tree_id = Some(
SHA1::from_str(tree_str.to_str().map_err(|e| {
GitError::InvalidCommit(format!("Invalid UTF-8 in tree SHA: {}", e))
})?)
.map_err(|e| GitError::InvalidCommit(format!("Invalid tree SHA: {}", e)))?,
);
} else if let Some(parent_str) = line.strip_prefix(b"parent ") {
parent_commit_ids.push(
SHA1::from_str(parent_str.to_str().map_err(|e| {
GitError::InvalidCommit(format!("Invalid UTF-8 in parent SHA: {}", e))
})?)
.map_err(|e| GitError::InvalidCommit(format!("Invalid parent SHA: {}", e)))?,
);
} else if line.starts_with(b"author ") {
author = Some(Signature::from_data(line.to_vec()).map_err(|e| {
GitError::InvalidCommit(format!("Invalid author signature: {}", e))
})?);
} else if line.starts_with(b"committer ") {
committer = Some(Signature::from_data(line.to_vec()).map_err(|e| {
GitError::InvalidCommit(format!("Invalid committer signature: {}", e))
})?);
}
}
let binding = commit[commit.find_byte(0x0a).unwrap() + 1..].to_vec();
commit = &binding;
let committer =
Signature::from_data(commit[..commit.find_byte(0x0a).unwrap()].to_vec()).unwrap();

let message = if message_start > 0 {
String::from_utf8_lossy(&data[message_start..]).to_string()
} else {
String::new()
// The rest is the message
let message = unsafe {
String::from_utf8_unchecked(commit[commit.find_byte(0x0a).unwrap() + 1..].to_vec())
};

Copilot AI Sep 10, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple calls to unwrap() throughout the parsing logic could cause panics on malformed commit data. The original code had proper error handling. Consider returning appropriate GitError::InvalidCommit variants instead of panicking.

Copilot uses AI. Check for mistakes.
Comment on lines +152 to 197
let tree_end = commit.find_byte(0x0a).unwrap();
let tree_id: SHA1 = SHA1::from_str(
String::from_utf8(commit[5..tree_end].to_owned()) // 5 is the length of "tree "
.unwrap()
.as_str(),
)
.unwrap();
let binding = commit[tree_end + 1..].to_vec(); // Move past the tree id
commit = &binding;

// Find the blank line that separates headers from the message
if let Some(pos) = headers.find(b"\n\n") {
message_start = pos + 2;
headers = &headers[..pos];
} else {
// If no blank line, the whole data is headers, no message
}
// Find the parent commit ids and remove them from the data
let author_begin = commit.find("author").unwrap();
// Find all parent commit ids
// The parent commit ids are all the lines that start with "parent "
// We can use find_iter to find all occurrences of "parent "
// and then extract the SHA1 hashes from them.
let parent_commit_ids: Vec<SHA1> = commit[..author_begin]
.find_iter("parent")
.map(|parent| {
let parent_end = commit[parent..].find_byte(0x0a).unwrap();
SHA1::from_str(
// 7 is the length of "parent "
String::from_utf8(commit[parent + 7..parent + parent_end].to_owned())
.unwrap()
.as_str(),
)
.unwrap()
})
.collect();
let binding = commit[author_begin..].to_vec();
commit = &binding;

let mut tree_id: Option<SHA1> = None;
let mut parent_commit_ids: Vec<SHA1> = Vec::new();
let mut author: Option<Signature> = None;
let mut committer: Option<Signature> = None;
// Find the author and committer and remove them from the data
// 0x0a is the newline character
let author =
Signature::from_data(commit[..commit.find_byte(0x0a).unwrap()].to_vec()).unwrap();

for line in headers.lines() {
if let Some(tree_str) = line.strip_prefix(b"tree ") {
tree_id = Some(
SHA1::from_str(tree_str.to_str().map_err(|e| {
GitError::InvalidCommit(format!("Invalid UTF-8 in tree SHA: {}", e))
})?)
.map_err(|e| GitError::InvalidCommit(format!("Invalid tree SHA: {}", e)))?,
);
} else if let Some(parent_str) = line.strip_prefix(b"parent ") {
parent_commit_ids.push(
SHA1::from_str(parent_str.to_str().map_err(|e| {
GitError::InvalidCommit(format!("Invalid UTF-8 in parent SHA: {}", e))
})?)
.map_err(|e| GitError::InvalidCommit(format!("Invalid parent SHA: {}", e)))?,
);
} else if line.starts_with(b"author ") {
author = Some(Signature::from_data(line.to_vec()).map_err(|e| {
GitError::InvalidCommit(format!("Invalid author signature: {}", e))
})?);
} else if line.starts_with(b"committer ") {
committer = Some(Signature::from_data(line.to_vec()).map_err(|e| {
GitError::InvalidCommit(format!("Invalid committer signature: {}", e))
})?);
}
}
let binding = commit[commit.find_byte(0x0a).unwrap() + 1..].to_vec();
commit = &binding;
let committer =
Signature::from_data(commit[..commit.find_byte(0x0a).unwrap()].to_vec()).unwrap();

let message = if message_start > 0 {
String::from_utf8_lossy(&data[message_start..]).to_string()
} else {
String::new()
// The rest is the message
let message = unsafe {
String::from_utf8_unchecked(commit[commit.find_byte(0x0a).unwrap() + 1..].to_vec())
};

Copilot AI Sep 10, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple calls to unwrap() throughout the parsing logic could cause panics on malformed commit data. The original code had proper error handling. Consider returning appropriate GitError::InvalidCommit variants instead of panicking.

Copilot uses AI. Check for mistakes.
Comment on lines +152 to 197
let tree_end = commit.find_byte(0x0a).unwrap();
let tree_id: SHA1 = SHA1::from_str(
String::from_utf8(commit[5..tree_end].to_owned()) // 5 is the length of "tree "
.unwrap()
.as_str(),
)
.unwrap();
let binding = commit[tree_end + 1..].to_vec(); // Move past the tree id
commit = &binding;

// Find the blank line that separates headers from the message
if let Some(pos) = headers.find(b"\n\n") {
message_start = pos + 2;
headers = &headers[..pos];
} else {
// If no blank line, the whole data is headers, no message
}
// Find the parent commit ids and remove them from the data
let author_begin = commit.find("author").unwrap();
// Find all parent commit ids
// The parent commit ids are all the lines that start with "parent "
// We can use find_iter to find all occurrences of "parent "
// and then extract the SHA1 hashes from them.
let parent_commit_ids: Vec<SHA1> = commit[..author_begin]
.find_iter("parent")
.map(|parent| {
let parent_end = commit[parent..].find_byte(0x0a).unwrap();
SHA1::from_str(
// 7 is the length of "parent "
String::from_utf8(commit[parent + 7..parent + parent_end].to_owned())
.unwrap()
.as_str(),
)
.unwrap()
})
.collect();
let binding = commit[author_begin..].to_vec();
commit = &binding;

let mut tree_id: Option<SHA1> = None;
let mut parent_commit_ids: Vec<SHA1> = Vec::new();
let mut author: Option<Signature> = None;
let mut committer: Option<Signature> = None;
// Find the author and committer and remove them from the data
// 0x0a is the newline character
let author =
Signature::from_data(commit[..commit.find_byte(0x0a).unwrap()].to_vec()).unwrap();

for line in headers.lines() {
if let Some(tree_str) = line.strip_prefix(b"tree ") {
tree_id = Some(
SHA1::from_str(tree_str.to_str().map_err(|e| {
GitError::InvalidCommit(format!("Invalid UTF-8 in tree SHA: {}", e))
})?)
.map_err(|e| GitError::InvalidCommit(format!("Invalid tree SHA: {}", e)))?,
);
} else if let Some(parent_str) = line.strip_prefix(b"parent ") {
parent_commit_ids.push(
SHA1::from_str(parent_str.to_str().map_err(|e| {
GitError::InvalidCommit(format!("Invalid UTF-8 in parent SHA: {}", e))
})?)
.map_err(|e| GitError::InvalidCommit(format!("Invalid parent SHA: {}", e)))?,
);
} else if line.starts_with(b"author ") {
author = Some(Signature::from_data(line.to_vec()).map_err(|e| {
GitError::InvalidCommit(format!("Invalid author signature: {}", e))
})?);
} else if line.starts_with(b"committer ") {
committer = Some(Signature::from_data(line.to_vec()).map_err(|e| {
GitError::InvalidCommit(format!("Invalid committer signature: {}", e))
})?);
}
}
let binding = commit[commit.find_byte(0x0a).unwrap() + 1..].to_vec();
commit = &binding;
let committer =
Signature::from_data(commit[..commit.find_byte(0x0a).unwrap()].to_vec()).unwrap();

let message = if message_start > 0 {
String::from_utf8_lossy(&data[message_start..]).to_string()
} else {
String::new()
// The rest is the message
let message = unsafe {
String::from_utf8_unchecked(commit[commit.find_byte(0x0a).unwrap() + 1..].to_vec())
};

Copilot AI Sep 10, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple calls to unwrap() throughout the parsing logic could cause panics on malformed commit data. The original code had proper error handling. Consider returning appropriate GitError::InvalidCommit variants instead of panicking.

Copilot uses AI. Check for mistakes.
Comment on lines +150 to +196
let mut commit = data;
// Find the tree id and remove it from the data
let tree_end = commit.find_byte(0x0a).unwrap();
let tree_id: SHA1 = SHA1::from_str(
String::from_utf8(commit[5..tree_end].to_owned()) // 5 is the length of "tree "
.unwrap()
.as_str(),
)
.unwrap();
let binding = commit[tree_end + 1..].to_vec(); // Move past the tree id
commit = &binding;

// Find the blank line that separates headers from the message
if let Some(pos) = headers.find(b"\n\n") {
message_start = pos + 2;
headers = &headers[..pos];
} else {
// If no blank line, the whole data is headers, no message
}
// Find the parent commit ids and remove them from the data
let author_begin = commit.find("author").unwrap();
// Find all parent commit ids
// The parent commit ids are all the lines that start with "parent "
// We can use find_iter to find all occurrences of "parent "
// and then extract the SHA1 hashes from them.
let parent_commit_ids: Vec<SHA1> = commit[..author_begin]
.find_iter("parent")
.map(|parent| {
let parent_end = commit[parent..].find_byte(0x0a).unwrap();
SHA1::from_str(
// 7 is the length of "parent "
String::from_utf8(commit[parent + 7..parent + parent_end].to_owned())
.unwrap()
.as_str(),
)
.unwrap()
})
.collect();
let binding = commit[author_begin..].to_vec();
commit = &binding;

let mut tree_id: Option<SHA1> = None;
let mut parent_commit_ids: Vec<SHA1> = Vec::new();
let mut author: Option<Signature> = None;
let mut committer: Option<Signature> = None;
// Find the author and committer and remove them from the data
// 0x0a is the newline character
let author =
Signature::from_data(commit[..commit.find_byte(0x0a).unwrap()].to_vec()).unwrap();

for line in headers.lines() {
if let Some(tree_str) = line.strip_prefix(b"tree ") {
tree_id = Some(
SHA1::from_str(tree_str.to_str().map_err(|e| {
GitError::InvalidCommit(format!("Invalid UTF-8 in tree SHA: {}", e))
})?)
.map_err(|e| GitError::InvalidCommit(format!("Invalid tree SHA: {}", e)))?,
);
} else if let Some(parent_str) = line.strip_prefix(b"parent ") {
parent_commit_ids.push(
SHA1::from_str(parent_str.to_str().map_err(|e| {
GitError::InvalidCommit(format!("Invalid UTF-8 in parent SHA: {}", e))
})?)
.map_err(|e| GitError::InvalidCommit(format!("Invalid parent SHA: {}", e)))?,
);
} else if line.starts_with(b"author ") {
author = Some(Signature::from_data(line.to_vec()).map_err(|e| {
GitError::InvalidCommit(format!("Invalid author signature: {}", e))
})?);
} else if line.starts_with(b"committer ") {
committer = Some(Signature::from_data(line.to_vec()).map_err(|e| {
GitError::InvalidCommit(format!("Invalid committer signature: {}", e))
})?);
}
}
let binding = commit[commit.find_byte(0x0a).unwrap() + 1..].to_vec();
commit = &binding;
let committer =
Signature::from_data(commit[..commit.find_byte(0x0a).unwrap()].to_vec()).unwrap();

let message = if message_start > 0 {
String::from_utf8_lossy(&data[message_start..]).to_string()
} else {
String::new()
// The rest is the message
let message = unsafe {
String::from_utf8_unchecked(commit[commit.find_byte(0x0a).unwrap() + 1..].to_vec())

Copilot AI Sep 10, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple intermediate Vec allocations and reassignments are inefficient. Consider using slice indices to track position in the original data instead of repeatedly copying and reallocating.

Copilot uses AI. Check for mistakes.
Comment on lines +181 to +182
let binding = commit[author_begin..].to_vec();
commit = &binding;

Copilot AI Sep 10, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple intermediate Vec allocations and reassignments are inefficient. Consider using slice indices to track position in the original data instead of repeatedly copying and reallocating.

Copilot uses AI. Check for mistakes.
Comment on lines +150 to +196
let mut commit = data;
// Find the tree id and remove it from the data
let tree_end = commit.find_byte(0x0a).unwrap();
let tree_id: SHA1 = SHA1::from_str(
String::from_utf8(commit[5..tree_end].to_owned()) // 5 is the length of "tree "
.unwrap()
.as_str(),
)
.unwrap();
let binding = commit[tree_end + 1..].to_vec(); // Move past the tree id
commit = &binding;

// Find the blank line that separates headers from the message
if let Some(pos) = headers.find(b"\n\n") {
message_start = pos + 2;
headers = &headers[..pos];
} else {
// If no blank line, the whole data is headers, no message
}
// Find the parent commit ids and remove them from the data
let author_begin = commit.find("author").unwrap();
// Find all parent commit ids
// The parent commit ids are all the lines that start with "parent "
// We can use find_iter to find all occurrences of "parent "
// and then extract the SHA1 hashes from them.
let parent_commit_ids: Vec<SHA1> = commit[..author_begin]
.find_iter("parent")
.map(|parent| {
let parent_end = commit[parent..].find_byte(0x0a).unwrap();
SHA1::from_str(
// 7 is the length of "parent "
String::from_utf8(commit[parent + 7..parent + parent_end].to_owned())
.unwrap()
.as_str(),
)
.unwrap()
})
.collect();
let binding = commit[author_begin..].to_vec();
commit = &binding;

let mut tree_id: Option<SHA1> = None;
let mut parent_commit_ids: Vec<SHA1> = Vec::new();
let mut author: Option<Signature> = None;
let mut committer: Option<Signature> = None;
// Find the author and committer and remove them from the data
// 0x0a is the newline character
let author =
Signature::from_data(commit[..commit.find_byte(0x0a).unwrap()].to_vec()).unwrap();

for line in headers.lines() {
if let Some(tree_str) = line.strip_prefix(b"tree ") {
tree_id = Some(
SHA1::from_str(tree_str.to_str().map_err(|e| {
GitError::InvalidCommit(format!("Invalid UTF-8 in tree SHA: {}", e))
})?)
.map_err(|e| GitError::InvalidCommit(format!("Invalid tree SHA: {}", e)))?,
);
} else if let Some(parent_str) = line.strip_prefix(b"parent ") {
parent_commit_ids.push(
SHA1::from_str(parent_str.to_str().map_err(|e| {
GitError::InvalidCommit(format!("Invalid UTF-8 in parent SHA: {}", e))
})?)
.map_err(|e| GitError::InvalidCommit(format!("Invalid parent SHA: {}", e)))?,
);
} else if line.starts_with(b"author ") {
author = Some(Signature::from_data(line.to_vec()).map_err(|e| {
GitError::InvalidCommit(format!("Invalid author signature: {}", e))
})?);
} else if line.starts_with(b"committer ") {
committer = Some(Signature::from_data(line.to_vec()).map_err(|e| {
GitError::InvalidCommit(format!("Invalid committer signature: {}", e))
})?);
}
}
let binding = commit[commit.find_byte(0x0a).unwrap() + 1..].to_vec();
commit = &binding;
let committer =
Signature::from_data(commit[..commit.find_byte(0x0a).unwrap()].to_vec()).unwrap();

let message = if message_start > 0 {
String::from_utf8_lossy(&data[message_start..]).to_string()
} else {
String::new()
// The rest is the message
let message = unsafe {
String::from_utf8_unchecked(commit[commit.find_byte(0x0a).unwrap() + 1..].to_vec())

Copilot AI Sep 10, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple intermediate Vec allocations and reassignments are inefficient. Consider using slice indices to track position in the original data instead of repeatedly copying and reallocating.

Copilot uses AI. Check for mistakes.
let parent_end = commit[parent..].find_byte(0x0a).unwrap();
SHA1::from_str(
// 7 is the length of "parent "
String::from_utf8(commit[parent + 7..parent + parent_end].to_owned())

Copilot AI Sep 10, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The slice bounds parent + parent_end is incorrect. parent_end is already an offset from commit[parent..], so this should be parent + 7..parent + 7 + parent_end or restructure the logic to avoid this confusion.

Suggested change
String::from_utf8(commit[parent + 7..parent + parent_end].to_owned())
String::from_utf8(commit[parent + 7..parent + 7 + parent_end].to_owned())

Copilot uses AI. Check for mistakes.
@genedna
genedna enabled auto-merge September 10, 2025 08:52
@genedna
genedna added this pull request to the merge queue Sep 10, 2025
Merged via the queue into gitmono-dev:main with commit bc1d59e Sep 10, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants