Skip to content
18 changes: 16 additions & 2 deletions src/authorship/diff_base.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
pub(crate) const EMPTY_TREE_SHA: &str = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
pub(crate) const SHA1_EMPTY_TREE: &str = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
pub(crate) const SHA256_EMPTY_TREE: &str =
"6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321";

pub(crate) fn empty_tree_for_oid(oid: &str) -> &'static str {
if oid.len() == 64 {
SHA256_EMPTY_TREE
} else {
SHA1_EMPTY_TREE
}
}

pub(crate) fn is_empty_tree_oid(oid: &str) -> bool {
oid == SHA1_EMPTY_TREE || oid == SHA256_EMPTY_TREE
}

/// Resolve the diff base for post-commit diff parsing so the diff is always
/// bounded to the single commit being finalized.
Expand All @@ -10,7 +24,7 @@ pub(crate) const EMPTY_TREE_SHA: &str = "4b825dc642cb6eb9a060e54bf8d69288fbee490
/// tree hash because there is no parent revision.
pub(crate) fn single_commit_diff_base(parent_sha: &str, commit_sha: &str) -> String {
if parent_sha == "initial" {
EMPTY_TREE_SHA.to_string()
empty_tree_for_oid(commit_sha).to_string()
} else {
format!("{commit_sha}^")
}
Expand Down
4 changes: 2 additions & 2 deletions src/authorship/post_commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,7 @@ pub(crate) fn post_commit_amend_with_recovery_timestamps_detailed(
| crate::authorship::background_agent::BackgroundAgent::WithHooks { .. }
) {
let diff_base = if parent_sha == "initial" {
"4b825dc642cb6eb9a060e54bf8d69288fbee4904"
crate::authorship::diff_base::empty_tree_for_oid(amended_commit)
} else {
&parent_sha
};
Expand Down Expand Up @@ -902,7 +902,7 @@ pub fn estimate_stats_cost_for_head(
.map(|p| p.id())
.unwrap_or_else(|_| "initial".to_string())
} else {
"4b825dc642cb6eb9a060e54bf8d69288fbee4904".to_string()
crate::authorship::diff_base::empty_tree_for_oid(commit_sha).to_string()
};
estimate_stats_cost_for_commit_range(repo, &parent_sha, commit_sha, ignore_patterns)
}
Expand Down
2 changes: 1 addition & 1 deletion src/authorship/range_authorship.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ fn create_authorship_log_for_range(

// Special handling for empty tree: there's no start state to compare against
// We only need the end state's attributions
if start_sha == EMPTY_TREE_HASH {
if crate::authorship::diff_base::is_empty_tree_oid(start_sha) {
tracing::debug!("Start is empty tree - using only end commit attributions");

let repo_clone = repo.clone();
Expand Down
21 changes: 12 additions & 9 deletions src/authorship/rewrite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ use crate::git::repository::{
Repository, exec_git, exec_git_allow_nonzero, exec_git_stdin_streaming,
};

const EMPTY_TREE_SHA: &str = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";

#[derive(Debug)]
pub enum RewriteEvent {
NonFastForward {
Expand Down Expand Up @@ -236,10 +234,6 @@ fn post_squash_metric_note_from_result(
}
}

fn empty_tree_sha() -> &'static str {
EMPTY_TREE_SHA
}

fn tree_revision_arg(sha: &str) -> Option<String> {
if sha == "initial" {
None
Expand All @@ -248,9 +242,13 @@ fn tree_revision_arg(sha: &str) -> Option<String> {
}
}

fn insert_known_tree(sha_to_tree: &mut HashMap<String, String>, sha: &str) -> bool {
fn insert_known_tree(
sha_to_tree: &mut HashMap<String, String>,
sha: &str,
empty_tree: &str,
) -> bool {
if sha == "initial" {
sha_to_tree.insert(sha.to_string(), empty_tree_sha().to_string());
sha_to_tree.insert(sha.to_string(), empty_tree.to_string());
true
} else {
false
Expand All @@ -277,9 +275,14 @@ fn resolve_tree_shas(
) -> Result<HashMap<String, String>, GitAiError> {
let mut sha_to_tree = HashMap::new();
let mut shas_to_resolve = Vec::new();
let empty_tree = unique_shas
.iter()
.find(|sha| sha.as_str() != "initial")
.map(|sha| crate::authorship::diff_base::empty_tree_for_oid(sha))
.unwrap_or(crate::authorship::diff_base::SHA1_EMPTY_TREE);

for sha in unique_shas {
if !insert_known_tree(&mut sha_to_tree, sha) {
if !insert_known_tree(&mut sha_to_tree, sha, empty_tree) {
shas_to_resolve.push(sha.clone());
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/authorship/stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,8 @@ pub fn stats_for_commit_stats_with_parent_and_authorship(
) -> Result<CommitStats, GitAiError> {
use crate::commands::diff::get_diff_with_line_numbers;

let from_ref = parent_sha.unwrap_or("4b825dc642cb6eb9a060e54bf8d69288fbee4904");
let from_ref =
parent_sha.unwrap_or_else(|| crate::authorship::diff_base::empty_tree_for_oid(commit_sha));
let hunks = get_diff_with_line_numbers(repo, from_ref, commit_sha)?;
stats_for_commit_stats_from_hunks(repo, commit_sha, ignore_patterns, &hunks, authorship_log)
}
Expand Down Expand Up @@ -686,7 +687,7 @@ pub fn get_git_diff_stats(
}

let from_ref = if parent_count == 0 {
"4b825dc642cb6eb9a060e54bf8d69288fbee4904".to_string()
crate::authorship::diff_base::empty_tree_for_oid(commit_sha).to_string()
} else {
commit_obj.parent(0)?.id()
};
Expand Down
2 changes: 1 addition & 1 deletion src/authorship/virtual_attribution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1222,7 +1222,7 @@ fn collect_committed_hunks(
// Handle initial commit (no parent)
if parent_sha == "initial" {
// For initial commit, use git diff against the empty tree
let empty_tree = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"; // Git's empty tree hash
let empty_tree = crate::authorship::diff_base::empty_tree_for_oid(commit_sha);
let added_lines = repo.diff_added_lines(empty_tree, commit_sha, pathspecs)?;

for (file_path, lines) in added_lines {
Expand Down
4 changes: 2 additions & 2 deletions src/commands/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,14 +455,14 @@ fn resolve_parent(repo: &Repository, commit: &str) -> Result<String, GitAiError>

if sha.is_empty() {
// No parent, this is initial commit - use empty tree
Ok("4b825dc642cb6eb9a060e54bf8d69288fbee4904".to_string())
Ok(crate::authorship::diff_base::empty_tree_for_oid(commit).to_string())
} else {
Ok(sha)
}
}
Err(_) => {
// No parent, this is initial commit - use empty tree hash
Ok("4b825dc642cb6eb9a060e54bf8d69288fbee4904".to_string())
Ok(crate::authorship::diff_base::empty_tree_for_oid(commit).to_string())
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/commands/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@ fn render_stats(
let parent_sha = parents
.first()
.map(String::as_str)
.unwrap_or("4b825dc642cb6eb9a060e54bf8d69288fbee4904");
.unwrap_or_else(|| crate::authorship::diff_base::empty_tree_for_oid(commit_sha));

if let Ok(estimate) = crate::authorship::post_commit::estimate_stats_cost_for_commit_range(
repo,
Expand Down
Loading
Loading