Compare commits
2 Commits
simplify-e
...
fix-hard-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1d193c049 | ||
|
|
e9162cc3e1 |
@@ -369,13 +369,18 @@ impl GitBlame {
|
|||||||
entries,
|
entries,
|
||||||
permalinks,
|
permalinks,
|
||||||
messages,
|
messages,
|
||||||
remote_url,
|
|
||||||
}) = blame.await?
|
}) = blame.await?
|
||||||
else {
|
else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
let entries = build_blame_entry_sum_tree(entries, snapshot.max_point().row);
|
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(
|
let commit_details = parse_commit_messages(
|
||||||
messages,
|
messages,
|
||||||
remote_url,
|
remote_url,
|
||||||
|
|||||||
@@ -20,30 +20,36 @@ pub struct Blame {
|
|||||||
pub entries: Vec<BlameEntry>,
|
pub entries: Vec<BlameEntry>,
|
||||||
pub messages: HashMap<Oid, String>,
|
pub messages: HashMap<Oid, String>,
|
||||||
pub permalinks: HashMap<Oid, Url>,
|
pub permalinks: HashMap<Oid, Url>,
|
||||||
pub remote_url: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Blame {
|
impl Blame {
|
||||||
pub fn for_path(
|
pub fn for_path<F>(
|
||||||
git_binary: &Path,
|
git_binary: &Path,
|
||||||
working_directory: &Path,
|
working_directory: &Path,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
content: &Rope,
|
content: &Rope,
|
||||||
remote_url: Option<String>,
|
|
||||||
provider_registry: Arc<GitHostingProviderRegistry>,
|
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 output = run_git_blame(git_binary, working_directory, path, content)?;
|
||||||
|
|
||||||
let mut entries = parse_git_blame(&output)?;
|
let mut entries = parse_git_blame(&output)?;
|
||||||
entries.sort_unstable_by(|a, b| a.range.start.cmp(&b.range.start));
|
entries.sort_unstable_by(|a, b| a.range.start.cmp(&b.range.start));
|
||||||
|
|
||||||
let mut permalinks = HashMap::default();
|
let mut permalinks = HashMap::default();
|
||||||
let mut unique_shas = HashSet::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() {
|
for entry in entries.iter_mut() {
|
||||||
unique_shas.insert(entry.sha);
|
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
|
// DEPRECATED (18 Apr 24): Sending permalinks over the wire is deprecated. Clients
|
||||||
// now do the parsing.
|
// now do the parsing.
|
||||||
if let Some((provider, remote)) = parsed_remote_url.as_ref() {
|
if let Some((provider, remote)) = parsed_remote_url.as_ref() {
|
||||||
@@ -66,7 +72,6 @@ impl Blame {
|
|||||||
entries,
|
entries,
|
||||||
permalinks,
|
permalinks,
|
||||||
messages,
|
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_COMMIT_ERROR: &str = "fatal: no such ref: HEAD";
|
||||||
const GIT_BLAME_NO_PATH: &str = "fatal: no such path";
|
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(
|
fn run_git_blame(
|
||||||
git_binary: &Path,
|
git_binary: &Path,
|
||||||
working_directory: &Path,
|
working_directory: &Path,
|
||||||
@@ -122,7 +177,7 @@ fn run_git_blame(
|
|||||||
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct BlameEntry {
|
pub struct BlameEntry {
|
||||||
pub sha: Oid,
|
pub sha: Oid,
|
||||||
|
pub remote_url: Option<String>,
|
||||||
pub range: Range<u32>,
|
pub range: Range<u32>,
|
||||||
|
|
||||||
pub original_line_number: u32,
|
pub original_line_number: u32,
|
||||||
|
|||||||
@@ -479,8 +479,8 @@ impl GitRepository for RealGitRepository {
|
|||||||
&working_directory,
|
&working_directory,
|
||||||
path,
|
path,
|
||||||
&content,
|
&content,
|
||||||
remote_url,
|
|
||||||
self.hosting_provider_registry.clone(),
|
self.hosting_provider_registry.clone(),
|
||||||
|
|remote_name| self.remote_url(remote_name),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2720,6 +2720,7 @@ fn serialize_blame_buffer_response(blame: Option<git::blame::Blame>) -> proto::B
|
|||||||
summary: entry.summary.clone(),
|
summary: entry.summary.clone(),
|
||||||
previous: entry.previous.clone(),
|
previous: entry.previous.clone(),
|
||||||
filename: entry.filename.clone(),
|
filename: entry.filename.clone(),
|
||||||
|
remote_url: entry.remote_url.clone(),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
@@ -2746,7 +2747,6 @@ fn serialize_blame_buffer_response(blame: Option<git::blame::Blame>) -> proto::B
|
|||||||
entries,
|
entries,
|
||||||
messages,
|
messages,
|
||||||
permalinks,
|
permalinks,
|
||||||
remote_url: blame.remote_url,
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2774,6 +2774,7 @@ fn deserialize_blame_buffer_response(
|
|||||||
summary: entry.summary,
|
summary: entry.summary,
|
||||||
previous: entry.previous,
|
previous: entry.previous,
|
||||||
filename: entry.filename,
|
filename: entry.filename,
|
||||||
|
remote_url: entry.remote_url,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@@ -2799,7 +2800,6 @@ fn deserialize_blame_buffer_response(
|
|||||||
entries,
|
entries,
|
||||||
permalinks,
|
permalinks,
|
||||||
messages,
|
messages,
|
||||||
remote_url: response.remote_url,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2230,25 +2230,26 @@ message BlameBuffer {
|
|||||||
|
|
||||||
message BlameEntry {
|
message BlameEntry {
|
||||||
bytes sha = 1;
|
bytes sha = 1;
|
||||||
|
optional string remote_url = 2;
|
||||||
|
|
||||||
uint32 start_line = 2;
|
uint32 start_line = 3;
|
||||||
uint32 end_line = 3;
|
uint32 end_line = 4;
|
||||||
uint32 original_line_number = 4;
|
uint32 original_line_number = 5;
|
||||||
|
|
||||||
optional string author = 5;
|
optional string author = 6;
|
||||||
optional string author_mail = 6;
|
optional string author_mail = 7;
|
||||||
optional int64 author_time = 7;
|
optional int64 author_time = 8;
|
||||||
optional string author_tz = 8;
|
optional string author_tz = 9;
|
||||||
|
|
||||||
optional string committer = 9;
|
optional string committer = 10;
|
||||||
optional string committer_mail = 10;
|
optional string committer_mail = 11;
|
||||||
optional int64 committer_time = 11;
|
optional int64 committer_time = 12;
|
||||||
optional string committer_tz = 12;
|
optional string committer_tz = 13;
|
||||||
|
|
||||||
optional string summary = 13;
|
optional string summary = 14;
|
||||||
optional string previous = 14;
|
optional string previous = 15;
|
||||||
|
|
||||||
string filename = 15;
|
string filename = 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
message CommitMessage {
|
message CommitMessage {
|
||||||
@@ -2266,7 +2267,6 @@ message BlameBufferResponse {
|
|||||||
repeated BlameEntry entries = 1;
|
repeated BlameEntry entries = 1;
|
||||||
repeated CommitMessage messages = 2;
|
repeated CommitMessage messages = 2;
|
||||||
repeated CommitPermalink permalinks = 3;
|
repeated CommitPermalink permalinks = 3;
|
||||||
optional string remote_url = 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
optional BlameResponse blame_response = 5;
|
optional BlameResponse blame_response = 5;
|
||||||
|
|||||||
Reference in New Issue
Block a user