Compare commits

...

2 Commits

Author SHA1 Message Date
Anthony Eid
e1d193c049 More work on fixing blame PR 2025-02-21 17:42:24 -05:00
Anthony Eid
e9162cc3e1 Set up ground work for getting correct remote branch per git blame entry
Co-authored-by: smit <0xtimsb@gmail.com>
2025-02-21 15:33:56 -05:00
5 changed files with 88 additions and 28 deletions

View File

@@ -369,13 +369,18 @@ impl GitBlame {
entries,
permalinks,
messages,
remote_url,
}) = blame.await?
else {
return Ok(None);
};
let entries = build_blame_entry_sum_tree(entries, snapshot.max_point().row);
let remote_url = entries.first().as_ref().and_then(|entry| {
entry
.blame
.as_ref()
.and_then(|blame| blame.remote_url.clone())
});
let commit_details = parse_commit_messages(
messages,
remote_url,

View File

@@ -20,30 +20,36 @@ pub struct Blame {
pub entries: Vec<BlameEntry>,
pub messages: HashMap<Oid, String>,
pub permalinks: HashMap<Oid, Url>,
pub remote_url: Option<String>,
}
impl Blame {
pub fn for_path(
pub fn for_path<F>(
git_binary: &Path,
working_directory: &Path,
path: &Path,
content: &Rope,
remote_url: Option<String>,
provider_registry: Arc<GitHostingProviderRegistry>,
) -> Result<Self> {
get_remote_url: F,
) -> Result<Self>
where
F: Fn(&str) -> Option<String>,
{
let output = run_git_blame(git_binary, working_directory, path, content)?;
let mut entries = parse_git_blame(&output)?;
entries.sort_unstable_by(|a, b| a.range.start.cmp(&b.range.start));
let mut permalinks = HashMap::default();
let mut unique_shas = HashSet::default();
let parsed_remote_url = remote_url
.as_deref()
.and_then(|remote_url| parse_git_remote_url(provider_registry, remote_url));
for entry in entries.iter_mut() {
unique_shas.insert(entry.sha);
let remote_name = get_remote_from_sha(git_binary, working_directory, entry.sha).ok();
let parsed_remote_url = remote_name
.and_then(|name| get_remote_url(&name))
.as_deref()
.and_then(|remote_url| parse_git_remote_url(provider_registry.clone(), remote_url));
// DEPRECATED (18 Apr 24): Sending permalinks over the wire is deprecated. Clients
// now do the parsing.
if let Some((provider, remote)) = parsed_remote_url.as_ref() {
@@ -66,7 +72,6 @@ impl Blame {
entries,
permalinks,
messages,
remote_url,
})
}
}
@@ -74,6 +79,56 @@ impl Blame {
const GIT_BLAME_NO_COMMIT_ERROR: &str = "fatal: no such ref: HEAD";
const GIT_BLAME_NO_PATH: &str = "fatal: no such path";
// git for-each-ref
// --sort=authordate
// --contains
// sha <97ed70db925576c37d73415245845a8a03e0bf08>
// --format='%(refname:short)' refs/remotes/ | head -n 1 | cut -d'/' -f1
fn get_remote_from_sha(git_binary: &Path, working_directory: &Path, sha: Oid) -> Result<String> {
let child = util::command::new_std_command(git_binary)
.current_dir(working_directory)
.arg("for-each-ref")
.arg("--sort=authordate")
.arg("--contains")
.arg(sha.0.to_string())
.arg("--format='%(refname:short)'")
.arg("refs/remotes/")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.map_err(|e| anyhow!("Failed to start git blame process: {}", e))?;
let mut stdin = child
.stdin
.as_ref()
.context("failed to get pipe to stdin of git blame command")?;
stdin.flush()?;
let output = child
.wait_with_output()
.map_err(|e| anyhow!("Failed to read git blame output: {}", e))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
let trimmed = stderr.trim();
if trimmed == GIT_BLAME_NO_COMMIT_ERROR || trimmed.contains(GIT_BLAME_NO_PATH) {
return Ok(String::new());
}
return Err(anyhow!("git blame process failed: {}", stderr));
}
let output = String::from_utf8(output.stdout)?;
let Some((output, _)) = output.split_once('/') else {
return Err(anyhow!("Couldn't find remote name"));
};
let output = output.split_at(1).1;
Ok(output.to_owned())
}
fn run_git_blame(
git_binary: &Path,
working_directory: &Path,
@@ -122,7 +177,7 @@ fn run_git_blame(
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
pub struct BlameEntry {
pub sha: Oid,
pub remote_url: Option<String>,
pub range: Range<u32>,
pub original_line_number: u32,

View File

@@ -479,8 +479,8 @@ impl GitRepository for RealGitRepository {
&working_directory,
path,
&content,
remote_url,
self.hosting_provider_registry.clone(),
|remote_name| self.remote_url(remote_name),
)
}

View File

@@ -2720,6 +2720,7 @@ fn serialize_blame_buffer_response(blame: Option<git::blame::Blame>) -> proto::B
summary: entry.summary.clone(),
previous: entry.previous.clone(),
filename: entry.filename.clone(),
remote_url: entry.remote_url.clone(),
})
.collect::<Vec<_>>();
@@ -2746,7 +2747,6 @@ fn serialize_blame_buffer_response(blame: Option<git::blame::Blame>) -> proto::B
entries,
messages,
permalinks,
remote_url: blame.remote_url,
}),
}
}
@@ -2774,6 +2774,7 @@ fn deserialize_blame_buffer_response(
summary: entry.summary,
previous: entry.previous,
filename: entry.filename,
remote_url: entry.remote_url,
})
})
.collect::<Vec<_>>();
@@ -2799,7 +2800,6 @@ fn deserialize_blame_buffer_response(
entries,
permalinks,
messages,
remote_url: response.remote_url,
})
}

View File

@@ -2230,25 +2230,26 @@ message BlameBuffer {
message BlameEntry {
bytes sha = 1;
optional string remote_url = 2;
uint32 start_line = 2;
uint32 end_line = 3;
uint32 original_line_number = 4;
uint32 start_line = 3;
uint32 end_line = 4;
uint32 original_line_number = 5;
optional string author = 5;
optional string author_mail = 6;
optional int64 author_time = 7;
optional string author_tz = 8;
optional string author = 6;
optional string author_mail = 7;
optional int64 author_time = 8;
optional string author_tz = 9;
optional string committer = 9;
optional string committer_mail = 10;
optional int64 committer_time = 11;
optional string committer_tz = 12;
optional string committer = 10;
optional string committer_mail = 11;
optional int64 committer_time = 12;
optional string committer_tz = 13;
optional string summary = 13;
optional string previous = 14;
optional string summary = 14;
optional string previous = 15;
string filename = 15;
string filename = 16;
}
message CommitMessage {
@@ -2266,7 +2267,6 @@ message BlameBufferResponse {
repeated BlameEntry entries = 1;
repeated CommitMessage messages = 2;
repeated CommitPermalink permalinks = 3;
optional string remote_url = 4;
}
optional BlameResponse blame_response = 5;